八皇后问题:分支界限法解决

文章详细描述了如何运用分支界限法来解决经典的n皇后问题,通过构建解空间树并利用队列进行层次遍历。在过程中,强调了与回溯法的相似性和不同点,特别是在处理无解情况如n=2或3时的优化。文章还探讨了方法的时间复杂度为O(n!)和较高的空间复杂度,并提出了这种方法可能不适合n皇后问题的观点。
摘要由CSDN通过智能技术生成

题目描述: 

 

(1)设计思想:分支界限法(题目明确要求)

(2)分析过程:

n皇后问题在上一章已经用回溯法介绍过了,课后也有在力扣上完成(我现在做的三道困难题两道是n皇后),这次用分支界限可以参考回溯法的思路,比如行用遍历表示,列和对角线需要额外判断,这些分析和回溯法大致相同。一样的解空间树(以n=3为例),层数表示行,节点数值表示列。

但实际操作时,发现两者区别较大。

因为要求对解空间树层序遍历,那么需要对每个可行节点所走的路径(或者说前面放置王后的位置)全部保存,那么需要用一个数组记录每个当前节点所走过的路径。再加上节点本身的位置,所以每个节点的数据结构应该有两部分,其本身数值(表示当前的深度)以及储存此前路径的数组。

类似树的层序遍历算法,一样使用队列进行层次遍历。但是由于节点的深度表示其行数,所以需要额外记录其深度,那么每完成一行的遍历(具体就是每n次)层数就要加1.

每次选择当前可以与前面节点共存的节点放入队列,每遍历完一层后,由于需要考虑当前树的深度,所以需要用一个额外的数字-1记录已进入下一层树了。

当遍历到最后一层时,一旦得到一个可行解就可以跳出死循环。并执行输出。

但是,对于n=2和n=3两个没有任何解的情况,以上思路编写的代码就会陷入死循环。这是不能接受的。所以循环每执行一次,用一个额外的数字记录循环次数。当换层时(即出现-1)该数清零。当该数字超过一定大数(本代码中使用了10000),可以认为陷入死循环,执行跳出操作。

(不过其实现在想想,这样有点本末倒置了,不如判断n=2或3就直接输出不可能。)

综合上述思路,完成代码

#include<bits/stdc++.h>
using namespace std;
struct bleak{//每个节点的数据结构,结构体本来也算一种类嘛
    int label;//表示行,就是当前在解空间树的深度
    vector<int>path;//表示前面放王后的位置,数值是列,序号是行
    bleak(){}
    bleak(int x,int y){//构造函数,其实不写也没关系
        label=x;
        path.push_back(y);
    }
};
bool queen(bleak a,int n,int y){//y是当前的层数
    for(int i=1;i<y;++i){
        if(a.path[i]==a.path[y]||abs(y-i)==abs(a.path[i]-a.path[y]))//列相等或者对角线
            return false;
    }
    return true;//可以放置
    }
void solveNQueens(int n){
    int bestx[n];//结果
    bool flag=false;//有没有结果
    queue<bleak>queens;
    int num=1,num2=0;//遍历的深度
    bleak a;//当前尝试的节点
    a.label=0;
    bleak next;
    next.label=-1;//标记进入下一层了
    queens.push(next);
    while(true){
        for(int i=1;i<=n;++i){
            bleak cur(num,0);//当前尝试的节点
            for(int j=1;j<num;++j){//把前面的点赋值给本节点
                cur.path.push_back(a.path[j]);
            }
            cur.path.push_back(i);//当前尝试的列数
            if(queen(cur,n,num)){//如果可行
                queens.push(cur);//加入队列
            }
        }
        a=queens.front();//取出队列头,开始遍历下一行的
        queens.pop();
        if(a.label==-1){
            num++;
            queens.push(next);//添加换层标记
            a=queens.front();//取出队列头,开始遍历下一行的
            queens.pop();
            num2=0;
        }
        if(a.label==n){//到最后一层了,题目要求就输出一个结果,所以单独写出来
            flag=true;
            for(int i=1;i<=n;++i){
                bestx[i]=a.path[i];
            }
            break;
        }
        if(num2>10000){//这个10000感觉在调参,实际上n=9时也可以了,足够了
            break;//无解时跳出死循环
        }
        num2++;
    }
    if(!flag){
        cout<<"n等于"<<n<<"时,无解!"<<endl;
        return;
    }
    for(int i=1;i<=n;++i){
        cout<<bestx[i]<<" ";
    }
}
int main()
{

    int n;
    cin>>n;
    solveNQueens(n);
    return 0;
}

时间复杂度:分支界限法对解空间树层次遍历,到达最后节点时已经遍历了n!次,故时间复杂度是O(n!)

空间复杂度:对于每个可能解都需要一个长度为num(当前深度)的额外数组记录,所以空间复杂度为O(nn)

结论:此问题明确要求使用分支界限法。主要还是依照回溯法和层次遍历的思路完成,用最普通的先进先出队列完成层次遍历。另外当无可能解时(n=2或3),额外判断了一下是否陷入死循环。这个还是很有必要的。

我个人感觉分支界限法不太适合解决n皇后问题,因为其空间复杂度有点高了,时间复杂度也没有优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
分支界限法(Branch and Bound)是一种常见的解决优化问题的算法。在使用分支界限法计算n皇后问题时,可以按照以下步骤进行: 1. 定义问题的状态空间:将一个n皇后问题的状态定义为一个n元组,其中第i个元素表示第i行皇后的列位置。因此,状态空间可以定义为所有这样的元组。 2. 定义问题的子问题:对于一个n皇后问题的状态,可以通过在当前状态的基础上,向下一行添加一个皇后,来得到一个新的状态。因此,问题的子问题可以定义为所有添加了一个皇后的状态。 3. 定义问题的约束条件:n皇后问题的主要约束条件是不能有两个皇后处于同一行、同一列或同一对角线。因此,在生成新状态时需要检查是否违反了这些约束条件。 4. 计算问题的下界:在分支界限法中,需要计算每个状态的下界,以便在搜索过程中能够尽早地剪枝。对于n皇后问题,可以使用启发式算法如启发式搜索来计算下界。 5. 搜索状态空间:按照分支界限法的基本思想,从初始状态开始搜索状态空间,对于每个状态,生成其子状态,并计算下界。如果某个状态的下界比当前最优解差,则将其剪枝;否则,将其加入搜索队列中继续搜索。 6. 输出最优解:当搜索结束时,得到的最优解即为n皇后问题的解。 需要注意的是,分支界限法虽然可以解决n皇后问题,但是在n较大时,搜索空间非常巨大,需要较长的时间才能得到解。因此,一些优化方法如剪枝、启发式搜索等可以用来加速计算过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值