回溯算法

回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。

用回溯算法解决问题的一般步骤为:

1、定义一个解空间,它包含问题的解。

2、利用适于搜索的方法组织解空间。

3、利用深度优先法搜索解空间。

4、利用限界函数避免移动到不可能产生解的子空间。

问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。

通常情况下,回溯法是通过递归来实现的,所以需要考虑递归的终止条件。同时当我们一条路走到“尽头”的时候,要后退到上一步,同时要消除当前这一步所产生的影响(一般是某些状态值,这是回溯的重点)。

回溯本质上是一种穷举法,在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算。只不过通常的题目中会有某些条件(比如最大、最小值的限制)可以供我们利用, 来进行剪枝,不去走那些肯定跟最终解没有关系的路径,从而提高效率。
 
光说不练假把式,下面来看一下,回溯法最经典的问题。

问题描述:

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

 

问题历史:
八皇后问题最早是由国际象棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。
 
解法思路:
(1)先从首位开始检查,如果不能放置,接着检查该行第二个位置,依次检查下去,直到在该行找到一个可以放置一个皇后的地方,然后保存当前状态,转到下一行重复上述方法的检索。
(2)如果检查了该行所有的位置均不能放置一个皇后,说明上一行皇后放置的位置无法让所有的皇后找到自己合适的位置,因此就要回溯到上一行,重新检查该皇后位置后面的位置。
 
代码实现:
#include <iostream>
#include <fstream>
using namespace std;

int count=0;
void solve (int linenum,int *LineSet,bool *column,bool *oblique1,bool *xie2,ofstream &os)
{
    for(int i=0;i<8;i++)
    {
        if(!column[i]&&!oblique1[linenum+i]&&!xie2[linenum-i+7])
        {
            LineSet[linenum]=i;
            column[i]=true;
            oblique1[linenum+i]=true;
            xie2[linenum-i+7]=true;
            if(linenum==7)
            {
                for(int k=0;k<8;k++)
                {
                    for(int j=0;j<8;j++)
                    {
                        if(j==LineSet[k])
                        {
                            os<<" Q"; 
                        }else
                        {
                            os<<" +";
                        }

                    }
                    os<<endl;
                }

                count++;
                os<<"这是第"<<count<<"种结果"<<endl;
                os<<endl;
                LineSet[linenum]=999;
                column[i]=false;
                oblique1[linenum+i]=false;
                xie2[linenum-i+7]=false;
                return;
            }
            else
            {
                solve(linenum+1,LineSet,column,oblique1,xie2,os);
                LineSet[linenum]=999;
                column[i]=false;
                oblique1[linenum+i]=false;
                xie2[linenum-i+7]=false;
                
            }
        }
        
    }
}

int main()
{
    int LineSet[8];
    bool column[8];
    bool oblique1[15];
    bool oblique2[15];
    for(int i=0;i<8;i++)
    {
        LineSet[i]=999;
        column[i]=false;
    }
    for(int i=0;i<15;i++)
    {
        oblique1[i]=false;
        oblique2[i]=false;
    }
    ofstream os("result.txt");
    solve(0,LineSet,column,oblique1,oblique2,os);
}

结果():

Q + + + + + + +
+ + + + Q + + +
+ + + + + + + Q
+ + + + + Q + +
+ + Q + + + + +
+ + + + + + Q +
+ Q + + + + + +
+ + + Q + + + +
这是第1种结果

Q + + + + + + +
+ + + + + Q + +
+ + + + + + + Q
+ + Q + + + + +
+ + + + + + Q +
+ + + Q + + + +
+ Q + + + + + +
+ + + + Q + + +
这是第2种结果

Q + + + + + + +
+ + + + + + Q +
+ + + Q + + + +
+ + + + + Q + +
+ + + + + + + Q
+ Q + + + + + +
+ + + + Q + + +
+ + Q + + + + +
这是第3种结果

Q + + + + + + +
+ + + + + + Q +
+ + + + Q + + +
+ + + + + + + Q
+ Q + + + + + +
+ + + Q + + + +
+ + + + + Q + +
+ + Q + + + + +
这是第4种结果

+ Q + + + + + +
+ + + Q + + + +
+ + + + + Q + +
+ + + + + + + Q
+ + Q + + + + +
Q + + + + + + +
+ + + + + + Q +
+ + + + Q + + +
这是第5种结果

...

+ + + + + + + Q
+ Q + + + + + +
+ + + + Q + + +
+ + Q + + + + +
Q + + + + + + +
+ + + + + + Q +
+ + + Q + + + +
+ + + + + Q + +
这是第90种结果

+ + + + + + + Q
+ + Q + + + + +
Q + + + + + + +
+ + + + + Q + +
+ Q + + + + + +
+ + + + Q + + +
+ + + + + + Q +
+ + + Q + + + +
这是第91种结果

+ + + + + + + Q
+ + + Q + + + +
Q + + + + + + +
+ + Q + + + + +
+ + + + + Q + +
+ Q + + + + + +
+ + + + + + Q +
+ + + + Q + + +
这是第92种结果

结果比较多,省略了一些,有兴趣可以自己编译运行一下程序代码,查看所有92个解。

原文地址:https://www.cnblogs.com/scarecrow-blog/p/3927585.html