八皇后问题:回溯法

回溯法思想:

当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函 数将返回上一级递归调用,这种现象称为回溯。正是因为这个原因,递归枚举算法常被称为 回溯法,应用十分普遍。

题目描述:

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

分析:

最简单的思路是把问题转化为“从64个格子中选一个子集”,使得“子集中恰好有8个格 子,且任意两个选出的格子都不在同一行、同

一列或同一个对角线上”。这正是子集枚举问 题。然而,64个格子的子集有264个,太大了,这并不是一个很好的模型。

第二个思路是把问题转化为“从64个格子中选8个格子”,这是组合生成问题。根据组合 数学,有 种方案,比第一种方案优秀,但仍然不够好。

经过思考,不难发现以下事实:恰好每行每列各放置一个皇后。如果用record[x]表示第x行 皇后的列编号,则问题变成了全排列生成问题。而0~7的排列一共只有8!=40320个,枚举量 不会超过它。

注意:在解决递归问题时,我们有时需要深入分析问题,对递归模型精雕细琢。一般还应 对解答树的结点数有一个粗略的估计,作为评价模型的重要依据,如图7-5所示。

 

图7-5中给出了四皇后问题的完整解答树。它只有17个结点,比4!=24小。为什么会这样 呢?这是因为有些结点无法继续扩展。例

如,在(0,2,*,*)中,第2行无论将皇后放到哪里,都 会和第0行和第1行中已放好的皇后发生冲突,其他还未放置的皇后更是如此。

在这种情况下,递归函数将不再递归调用它自身,而是返回上一层调用,这种现象称为 回溯(backtracking)。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000;
bool vis[2][maxn];
int record[maxn][maxn];
int tot, n;
void search(int cur) {
    if(cur==n) tot++;//tot答案个数
    else {
        for(int i = 0; i < n; i++) {
            //不在一列以及一个对角线处
	    if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) {
	    	//标记
                    vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = true;
           	    record[tot][cur] = i;//第tot记录在record中
		    search(cur+1);
                    //回溯要注意清除标记
	            vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = false;
	    }
	}
    }
}


int main() {
    // freopen("i.txt","r",stdin);
    // freopen("o.txt","w",stdout);
    tot = 0;
    cin >> n;//确定棋盘大小
    memset(vis, false, sizeof(vis));
    memset(record, 0, sizeof(record));
    search(0);
    cout << tot << endl;
    for(int i = 0; i < tot; i++) {
    	for(int j = 0; j < n; j++) {
    	    if(record[j]) //打印每种情况的n个皇后
           	cout << j << " " << record[i][j] << endl;
    	}	
    	cout << "-------------------------------------" << endl;
    }
    return 0;
}

上面的程序有个极其关键的地方:vis数组的使用。vis数组的确切含义是什么?它表示 已经放置的皇后占据了哪些列、主对角线

和副对角线。将来放置的皇后不应该修改这些值——至少“看上去没有修改”。一般地,如果在回溯法中修改了辅助的全局变量,则一定要及 时把它们恢复原状(除非故意保留所做修改)。若不信,可以把“vis[0][i]= vis[1][cur+i] = vis[2][cur-i+n] = 0”注释掉,

验证还能否正确求解八皇后问题。另外,在调用之前一定要把 vis数组清空。

如果在回溯法中使用了辅助的全局变量,则一定要及时把它们恢复原状。 特别地,若函数有多个出口,则需在每个出口处恢复被修改的值。

抄自紫书

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值