回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路一、问题在nxn格的棋盘上放置彼此不受攻击的n格皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在nxn格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。二、算法与分析用数组x[i](1≤i≤n)表示n后问题的解。其中x[i]表示皇后i放在棋盘的第i行的第x[i]列。由于不允许将2个皇后放在同一列,所以解向量中的x[i]…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一、问题

在nxn格的棋盘上放置彼此不受攻击的n格皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在nxn格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。

二、算法与分析

用数组x[i](1≤i≤n)表示n后问题的解。其中x[i]表示皇后i放在棋盘的第i行的第x[i]列。由于不允许将2个皇后放在同一列,所以解向量中的x[i]互不相同。2个皇后不能放在同一斜线上是问题的隐约束。对于一般的n后问题,这一隐约束条件可以化成显约束形式。设2个皇后放置位置为(i,j),(k,l):

显然,棋盘的每一行上可以而且必须摆放一个皇后,所以,n皇后问题的可能解用一个n元向量X=(x1, x2, …, xn)表示,其中,1≤i≤n并且1≤xi≤n,即第i个皇后放在第i行第xi列上

由于两个皇后不能位于同一列上,所以,解向量X必须满足约束条件:

                             xi≠xj                                   (式1)

若两个皇后摆放的位置分别是(i, xi)和(j, xj),在棋盘上斜率为-1的斜线上,满足条件i-j= xi-xj,在棋盘上斜率为1的斜线上,满足条件i+j= xi+xj,综合两种情况,由于两个皇后不能位于同一斜线上,所以,解向量X必须满足约束条件:

                             |i-xi|≠|j-xj|                                  (式2)

为了简化问题,下面讨论四皇后问题。

四皇后问题的解空间树是一个完全4叉树,树的根结点表示搜索的初始状态,对应Backtrack(1,x);从根结点到第2层结点对应皇后1在棋盘中第1行的可能摆放位置,从第2层结点到第3层结点对应皇后2在棋盘中第2行的可能摆放位置,依此类推。

完全4叉树,我只画了一部分,完整的应该是除了叶结点,每个内部结点都有四个子结点,k表示层数:

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

剪枝之后:

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

回溯法求解4皇后问题的搜索过程:

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

当然这个图只表示到找到的第一个解,我们知道还有另外一个解。

三、c++代码

变量sum记录可行方案个数,初始为1;

n表示皇后个数,由用户输入;

x[]数组保存问题的解,表示皇后i放在棋盘的第i行第x[i]列,初始时各元素都为0,而我们目的是求出有多少组(x[1],x[2],x[3]……x[n])满足摆放条件;

output(int x[])函数作用是输出当前找到的一个可行解,只在搜索到叶节点时才会调用;

Place(int k,int x[])函数作用是,对当前行k以上的所有行(即1到k-1行)逐行进行检查,如果该行与上面任何一行相互攻击(即位于同一对角线上了或同列了:abs(i-k)==abs(x[i]-x[k]) || x[i]==x[k]),那么返回false,否则返回true;

Backtrack(int k,int  x[])函数表示搜索解空间中第k层子树,k>n时,算法搜索至叶节点,得到一个新的n皇后互不攻击放置方案,那么输出该方案,可行方案数sum加1;k<=n时,当前扩展节点是解空间的内部节点,该节点有x[1],x[2],x[3]……x[n]共n个子节点,对每一个子节点,用函数Place检查其可行性,如果可行,以深度优先的方式递归地对可行子树搜索,如果不可行剪枝。

#include <iostream>
#include <cmath>
using namespace std;
int sum=0;
int n;

int output(int  x[]){
    int i;
    for(i=1;i<=n;i++){
      cout << "(" << i << "," << x[i] << ")" << " ";                                   
    }
    cout << endl;
    return 0;
}


bool Place(int k,int  x[]){
     int i;
     for(i=1;i<k;i++){
     if(abs(i-k)==abs(x[i]-x[k]) || x[i]==x[k])
         return false;                     
     } 
     return true;
     
}
     
     
     
int Backtrack(int k,int  x[]){
    int i;
     if(k>n){//如果是叶节点,直接输出找到的一个解 
          output(x);
          sum++; 
     }
     else{//内部节点,如果满足约束条件,继续深度搜索 。i代表列数,从1到n 
         for(i=1;i<=n;i++){
               x[k]=i;
               if(Place(k,x))
               Backtrack(k+1,x);
         } 
     }
     
     
}

int main(){
    int *x,i;    
    cout << "输入皇后个数:" << endl;
    cin >> n;
    cout << endl;
    x=new int[n+1];
    for(i=0;i<=n;i++){
      x[i]=0;                
    }    
    Backtrack(1,x);
    cout << endl;
    cout << "解的个数:" << sum << endl;
    system("pause"); 
    return 0;
}

以上的程序易于理解,但如果表示成非递归方式,可进一步省去O(n)递归栈空间,使用非递归的迭代回溯法:

#include <iostream>
#include <cmath>
using namespace std;
int sum=0;
int n;

int output(int  x[]){
    int i;
    for(i=1;i<=n;i++){
      cout << "(" << i << "," << x[i] << ")" << " ";                                   
    }
    cout << endl;
    return 0;
}


bool Place(int k,int  x[]){
     int i;
     for(i=1;i<k;i++){
     if(abs(i-k)==abs(x[i]-x[k]) || x[i]==x[k])
         return false;                     
     } 
     return true;
     
}
     
     
     
void Backtrack(int k,int  x[]){
    x[k]=0;
    while(k>0){
        x[k]+=1;
        while(x[k]<=n && !Place(k,x)) x[k]++;   //找到第k行满足约束条件的那一列,以便对子结点继续深度搜索 
        if(x[k]<=n){//找到了满足条件的子结点 
             if(k==n){//是叶结点 
                   output(x);
                   sum++;  
             }else{
                  k++;
                  x[k]=0; 
             }
                    
        }else{//对该行各列已经检查完 
             k--; 
        }  
           
            
    }
          
}

int main(){
    int *x,i;    
    cout << "输入皇后个数:" << endl;
    cin >> n;
    cout << endl;
    x=new int[n+1];
    for(i=0;i<=n;i++){
      x[i]=0;                
    }    
    Backtrack(1,x);
    cout << endl;
    cout << "解的个数:" << sum << endl;
    system("pause"); 
    return 0;
}

运行结果:

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

再测试下程序,n输入其他值:

n=8有92种,n=12有14200种。

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

回溯法之n皇后问题总结_用回溯法求解n皇后问题的思路

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/187598.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 别让你的敏感词过滤系统把正常的文字过滤了!

    别让你的敏感词过滤系统把正常的文字过滤了!昨天csdn上发了一段文字评论(绝对非常规矩的文字),结果提示有敏感词,然后直接把我的博客和谐掉了。访问博客时提示:我们不去探讨过滤所谓敏感词的必要性和意义。既然做了这个系统,就要保证过滤质量,该过滤的过滤,不该过滤的词汇就不要过滤。如下这段话,作者实在没看出那里有不和谐因素。请读者指教啊:PS:会不会因为发了这篇文章博客再次被关闭啊!补充:经

    2022年5月10日
    35
  • ASSERT_VALID_assert语句

    ASSERT_VALID_assert语句ASSERT()ASSERT()被测试它的参数,若参数为0,则中断执行并打印一段说明消息。在Release版本的程序中它不起任何作用。ASSERT()使用的时候必须保证参数表达式中不能有函数调用(译者注:ASSERT()宏在Release版本中不对表达式求值),因此对于任何有函数调用的参数表达式,应该使用宏VERIFY(),以保证表达式中的函数调用在Release版本中会被正确求值…

    2022年9月6日
    7
  • 【剑指offer】设置在最小数目的阵列

    【剑指offer】设置在最小数目的阵列

    2021年12月31日
    42
  • 本土化Linux系统,科学网—linux本地化进行lefse分析 – 林国鹏的博文

    本土化Linux系统,科学网—linux本地化进行lefse分析 – 林国鹏的博文注:参考来自网络,如侵权则删。##对应于上述A-F6个模块,本地版的命令行操作示例如下#A,设置LEfSe的数据格式,详情format_input.py-h#-c,指定class的行(必须指定);-s,指定sub_class的行(可缺省);#-u,指定subject_id的行(可缺省);-o,设置归一化值,默认-1即不执行标准化#注:版本问题,有时format_in…

    2022年6月4日
    45
  • Eclipse使用之导入Maven项目详解[通俗易懂]

    Eclipse使用之导入Maven项目详解[通俗易懂]通俗的来说,Maven就是个类似于git的项目管理工具。而SpringMVC就是将M(Model)、V(View)、C(Controller)三者进行分离进行处理,更有利于开发的进行。下面我将介绍一个别人已经编译好的Maven项目扔给你应该怎样导入到集成开发环境中。开发环境:EclipseStep1:在Eclipse中,选择File->Import;接着如下图所示:点击Browse,选择

    2022年5月29日
    26
  • 狂神说Linux_狂神说博客园

    狂神说Linux_狂神说博客园Linux在服务器端,很多大型项目都是部署在Linux服务器上利用VM + Centos7搭建本地Linux系统你可以使用 man [命令]来查看各个命令的使用文档,如 :man cp。概念云服务器就是一个远程电脑Linux中一切皆文件根目录/,所有的文件都挂载在这个节点下/bin:bin是Binary的缩写, 这个目录存放着最经常使用的命令。/boot: 这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。/dev : dev是Device(设备

    2022年8月9日
    7

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号