回溯法之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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • H3C交换机常用命令汇总

    H3C交换机常用命令汇总H3C交换机常用命令1.查看Linux下查看端口状态 root@root:~# netstat -an|grep -E “6002|6003″2.H3C交换机显示当前配置 [H3C]display current-configuration3.H3C交换机显示arp信息 [H3C]dis arp4.H3C交换机显示mac列表信息 [H3C]dis mac-address5.H3C交换机显示端口信息

    2022年6月20日
    50
  • 如何将a4排版成a3双面打印_A4如何双面打印

    如何将a4排版成a3双面打印_A4如何双面打印A4排成A3双面打印怎么操作?A3纸张的尺寸是297mm×420mm,其大小相当于两张A4的大小,A4是大家工作及生活中使用较多的纸张尺寸,A3纸张不常用,但是遇到一些比较重要的画报、图纸等之类的资料,A3纸张就比较突出了。在城市周边打印店,打印资料时多以使用A4纸张居多,所以如果您到打印店打印A3纸张,很有可能会被打印店告知:无法打印。有时候可能不是打印店员工不会帮您排版,而是打印店的设备不支持为大家打印A3大小的纸张资料。今天小编给大家介绍一个比较专业的网上在线打印平台——易桌面打印室,这是一个网

    2022年9月6日
    3
  • 深圳IT外包公司名单汇总

    深圳IT外包公司名单汇总开科唯识汉克时代拓维云创旭阳软件赛意信息金证股份博颜科技得逸信息新致软件兴融联通通互联信必优易宝长亮科技紫川软件文思海辉东软睿服科技拓保软件联龙汉克润和三丈信息信达体育文化京北方佰钧成亿达新致华云信息纬创软件合生科技海万信息Pactura维沃法本德科中软国际软通动力大展科技天阳博奥特先进数通融安易立德人瑞云盈网络中科软科锐国际湃腾点点新致煜象科技泛鹏天地…

    2022年6月3日
    93
  • idhttp的socket error # 10054 错误的处理办法

    idhttp的socket error # 10054 错误的处理办法在通过http实现restful数据通讯时,死活出现:socketerror#10054导致这种情况的原因很复杂。同样的程序,在不同的环境中出现不同结果。通过观察,发现登录后获取toke

    2022年7月3日
    22
  • [职场]最近聊到30岁以上的程序员,该何去何从了?你有啥想法?

    [职场]最近聊到30岁以上的程序员,该何去何从了?你有啥想法?

    2022年2月19日
    41
  • 计算机网络谢希仁第七版 课后答案

    计算机网络谢希仁第七版 课后答案谢希仁计算机网络第七版课后答案第一章概述1-01计算机网络向用户可以提供那些服务?答:连通性和共享1-02简述分组交换的要点。答:(1)报文分组,加首部(2)经路由器储存转发(3)在目的地合并1-03试从多个方面比较电路交换、报文交换和分组交换的主要优缺点。答:(1)电路交换:端对端通信质量因约定了通信资源获得可靠保障,对连续传送大量数据效率高。(2)报文交换:无须预约传输带…

    2022年5月3日
    47

发表回复

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

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