回溯法及N皇后问题

Posted 延禾xy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回溯法及N皇后问题相关的知识,希望对你有一定的参考价值。

reference:

http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html

http://www.cnblogs.com/Creator/archive/2011/05/20/2052341.html

1、什么是回溯法

 回溯法是一种系统地搜索问题解答的方法。在搜索的过程中尝试找到问题的解,如果发现找不到了,就退一步,往上回溯(剪枝过程)。对于许多复杂问题和大规模问题都可以使用回溯法。
 回溯法的基本思想是按照深度优先搜索的策略,从根节点开始搜索,当到某个节点时要判断是否是包含问题的解,如果包含就从该节点继续搜索下去,如果不包含,就向父节点回溯。若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
  回溯法常用的剪枝函数:(1)约束函数:在节点处减去不满足约束的子树。(2)界限函数:减去得不到最优解的子树

2、回溯法解题的一般步骤

  1. 针对所给问题,确定问题的解空间
  2. 利用适于搜索的方法组织解空间
  3. 利用深度优先搜索解空间
  4. 在搜索过程中用剪枝函数避免无效搜索。

3、算法框架

  • 设问题的解是一个n维向量(a1,a2,………,an),约束条件是ai(i=1,2,3,…..,n)之间满足某种条件,记为f(ai)。
  • 非递归回溯框架
int a[n], i;
初始化a[n];
i = 1;
while (i > 0(有路可走) and (未达到目标))//还没有回溯到头

    if (i > n)
    
        搜索到一个解,输出;
    
    else
    
        a[i]第一个可能的值;
        while (a[i]在不满足约束条件且在搜索空间内)
        
            a[i]下一个可能的值;
        
        if (a[i]在搜索空间内)
        
            标识占用的资源;
            i = i + 1;  //扩展下一个节点
        
        else
        
            清理所占的空间状态;  //回溯
            i = i - 1;
        
    

  • 递归回溯框架
int a[n];
try(int i)

    if(i>n)
        输出结果;
    else
   
    for(j = 下界; j <= 上界; j=j+1)  // 枚举i所有可能的路径
       
            if(fun(j))                 // 满足限界函数和约束条件
              
                 a[i] = j;
               ...                         // 其他操作
                 try(i+1);
              回溯前的清理工作(如a[i]置空值等);
              
              
          
 

4举例:N皇后问题

在一个N*N的棋盘上放置N个皇后,每行一个并使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击)。

/*
用n元组x[1:n]表示n后问题的解。x[i]表示皇后i放置在棋盘的第i行的第x[i]列
*/


#include <stdio.h>
#include <math.h>
#include <stdlib.h>

static int n, x[1000];
static long sum;

/*
判断第k个后能不能放在x[k]处
两个皇后不能放在统一斜线上:
若2个皇后放置的位置分别是(i,j)和(k,l),
且 i-j = k -l 或 i+j = k+l,则说明这2个皇后处于同一斜线上。
*/

void OutPut()

    for (int i = 1; i <= n; ++i)
        printf("(%d, %d) ", i, x[i]);
    printf("\\n");


int Place(int k)

    for (int j = 1; j < k; ++j)
        if (abs(k - j) == abs(x[k] - x[j]) || x[j] == x[k])
            return 0;
    return 1;


void BackTrack1(int t)

    //如果t>n说明已经完成一次放置
    if (t > n)
    
        sum++;
        OutPut();
    
    else
    
        for (int i = 1; i <= n; ++i)
        
            x[t] = i;
            if (Place(t))                   //可以放在i位置处,则继续搜索
                BackTrack1(t + 1);
        
    


void BackTrack()

    int k;
    x[1] = 0;       //初始化为0
    k = 1;
    while (k >= 1)  //循环
    
        x[k] += 1;  //先放在第一个位置
        while ((x[k] <= n) && !(Place(k)))  //如果不能放
            x[k] += 1;                      //放在下一个位置
        if (x[k] <= n)                      //放置完成
        
            if (k == n)                     //如果已经放完了n个皇后
            
                sum++;                      //处理次数,输出
                OutPut();
            
            else                            //没有处理完,让k自加,处理下一个皇后
            
                k++;
                x[k] = 0;
            
                                           //x[k] > n,说明没有合适的位置了
        else
            k--;                            //回溯回去,回到第k-1步
    


int main()

    clock_t start, finish;
    double duration;

    int nn;
    while (scanf_s("%d", &nn) != EOF)
    
        n = nn;
        sum = 0;
        for (int i = 0; i <= n; ++i)
            x[i] = 0;
        BackTrack();
        //BackTrack1(1);
        printf("%d\\n", sum);
    

    return 0;


以上是关于回溯法及N皇后问题的主要内容,如果未能解决你的问题,请参考以下文章

暴力穷举和回溯法(八皇后问题)

N皇后问题(递归回溯)

N皇后问题—初级回溯

求教C语言回溯法写出八皇后问题的92种解

回溯法-N皇后问题-C++算法

Python生成器回溯和八皇后问题