第1关:回溯法-n皇后问题

任务描述

本关任务:理解并实现

  1. 理解回溯;
  2. 实现n皇后 ;

相关知识

为了完成本关任务,你需要掌握:

  1. c++基础;
  2. 回溯;
回溯法

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

基本思想

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。

若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

用回溯法解题的一般步骤:

(1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解; (2)确定结点的扩展搜索规则; (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

(n皇后问题)

八皇后问题:在8*8的棋盘上放置八个皇后,使得它们互不攻击,此时每个皇后的攻击的攻击范围为同行同列和同对角线,要求找出所有解。 n皇后问题:在棋盘上n*n的棋盘上放置n个皇后,使它们互不攻击,找出所有解。

例题分析

以4皇后为例,其他的N皇后问题以此类推。所谓4皇后问题就是求解如何在4×4的棋盘上无冲突的摆放4个皇后棋子。在国际象棋中,皇后的移动方式为横竖交叉的,因此在任意一个皇后所在位置的水平、竖直、以及45度斜线上都不能出现皇后的棋子,例子

,

回溯法的基本思想是:可以构建出一棵解空间树,通过探索这棵解空间树,可以得到四皇后问题的一种或几种解。这样的解空间树有四棵

在如上图所示的4×4的棋盘上,按列来摆放棋子,首先因为皇后棋子不能在同一列,所以先排除有2个或2个以上的棋子在同一列的情况,所以第一个棋子在第一列有4种摆放方法(第1列第1行,第1列第2行,第1列第3行,第1列第4行),同样第二个棋子在第二列有4种,同样第三个棋子在第三列有4种,同样第四个棋子在第四列有4种,所以进行简单的排除不在同一列的情况后,还有4×4×4×4=256种可能,但是在这256种可能里,依然存在比如棋子在同一行,或在45度斜线上的情况出现。另一个角度思考,所有的满足四皇后问题的摆放方式一定都存在于这256种情况之中。简单的理解就是:这256种棋盘局面包含了所有满足4皇后问题的解,但是不包含全部的棋盘局面。

下面是解空间树的示例(以上一段的按列摆放的方式来进行示例讲解),其中第i层的棋盘局面是在第i-1层的棋盘局面演化而来的(1<i<4)

,

上面的图片是以第一个棋子在第一列的第一行而派生出的一个解空间树,最后一层会有64中结局面,同理在以第一个棋子在第一、列的第二/三/四行都分别可以派生出一个解空间树,最后一层都会有64中局面,所以有4棵解空间树,每一棵最终有64个局面,所以一共有4×64=256种局面。

可以用上面的方法穷举出所有的解,再遍历穷举的所有结果找出所有符合四皇后问题的解,但是这样会很浪费。所以这里可以用到回溯法,在构建解空间树的途中进行深度优先探索,当探索到某一种棋盘局面一定不是四皇后问题的解的时候(比如出现任意两个或两个以上的棋子在同一行/同一列/45度斜线上),就可以判断这个节点向下派生出的解空间树的节点也一定不是四皇后问题的解,这样就可以避免大量的无用功。

比如上图中第二行的第一个节点出现了两个棋子在同一行的情况,所以可以判断出这个节点以及这个节点向下派生出的所有节点就不再有必要进行遍历了,这样就会避免4+4×4次的完全无用功的遍历,就会大大的节省时间,再去探索第二行的第二个节点……其他的同理。

这样,如果能够成功遍历到叶子节点,并且判断该叶子节点的局面就是符合4皇后问题的,那么这个节点局面就代表一个合法的四皇后问题的解。下面的图片就代表找到的一个合法的解的过程(注意图片中,虚线代表排除,黑实线代表继续向下探索) 以上图为例,当在第i层出现非法的棋盘局面时,就跳回第i-1层,继续探索第i-1层的那个节点的下一个分支;或者在第4层探索到合法的局面就进行记录并跳回上一层,继续探索下一个分支。其他三个解空间树同理。

,

以上图为例,就单看探索的第四层节点的个数。使用回溯法,就只需探索第4层中的4个节点,而如果使用穷举法,就要探索玩第4层的所有64个节点,显而易见,哪一个方法更有效。

其实在解决四皇后问题的时候,并不一定要真的构建出这样的一棵解空间树,它完全可以通过一个递归回溯来模拟。所谓的解空间树只是一个逻辑上的抽象。当然也可以用树结构来真实的创建出一棵解空间树,不过那样会比较浪费空间资源,也没有那个必要。

编程要求

根据提示,在右侧编辑器补充代码,完成n皇后问题。

测试说明

平台会对你编写的代码进行测试:

输入一个值,n。表示n个皇后也是n*n的棋盘。 输出一个数,有多少种解。

测试输入:

 
  1. 4

预期输出:

 
  1. 2

测试输入:

 
  1. 8

预期输出:

 
  1. 92

开始你的任务吧,祝你成功

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
/********** Begin **********/
// 定义全局结构或函数
int tot=0;
void dfs(int cur,int n,int* c){
    if(cur==n){
        tot++;
    }else for(int i=0;i<n;i++){
        int ok=1;
        c[cur]=i;
        for(int j=0;j<cur;j++){
            if(c[cur]==c[j] || cur-j==c[cur]-c[j] || cur-j==c[j]-c[cur]){
                ok=0;
                break;
            }
            
        }
        if(ok) dfs(cur+1,n,c);
    }
}
/********** End **********/
int main(){
    /********** Begin **********/
    int n;
    cin>>n;
    int c[n];
    dfs(0,n,c); 
    cout<<tot<<endl;
    /********** End **********/
    return 0;
}

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回溯解0_1背包问题时,会用到状态空间树。在搜索状态空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r≤bestp时,可剪去右子树。计算右子树中解的上界可以用的方是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界,用此值来剪枝。 为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考察各物品即可。在实现时,由MaxBoundary函数计算当前结点处的上界。它是类Knap的私有成员。Knap的其他成员记录了解空间树种的节点信息,以减少函数参数的传递以及递归调用时所需要的栈空间。在解空间树的当前扩展结点处,仅当要进入右子树时才计算上界函数MaxBoundary,以判断是否可以将右子树减去。进入左子树时不需要计算上界,因为其上界与父结点的上界相同。 在调用函数Knapsack之前,需要先将各物品依其单位重量价值从达到小排序。为此目的,我们定义了类Objiect。其中,运算符与通常的定义相反,其目的是为了方便调用已有的排序算。在通常情况下,排序算将待排序元素从小到大排序。 在搜索状态空间树时,由函数Backtrack控制。在函数中是利用递归调用的方实现了空间树的搜索
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暴躁的梁小忠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值