回溯法之八皇后问题

八皇后问题就是在一个8*8的棋盘上任何两列的行、列、对角线上都不允许有其他元素,问在棋盘上放8个棋子共有多少种放法?

题目不难,想必很多人都有解决思路。但是你的解决思路是最简的吗?

假如第一行放一个那么第二行有7种放法,然后第3行又有6种放法,以此类推,共有8!=40320个。

递归都会写,关键是判断条件,如何简化判断。

int tot = 0; int c[100];
void search(int cur) {
	if (cur == n) { 
		tot++; //累积多少种放法
		for (int i = 0; i < n; i++)cout << c[i] << " ";//输出可行的排列
		cout << endl;
	}
	else for (int i = 0; i < n; i++) {
		int ok = 1;
		c[cur] = i;//将i列赋给cur行
		for (int j = 0; j < cur; j++) {
			if (c[cur] == c[j] || cur - c[cur] == j - c[j] || cur + c[cur] == j + c[j]) {//判断是否有对角线上存在或者行上存在
				ok = 0; break;
			}
		}
		if (ok)search(cur + 1);//递归下一行
	}
}

第12行是判断是否可行语句,本来我们的判断可能就是建立一个二维数组找出这个点所在的列是否有其他元素和它两个对角线上是否有其他元素(方法就是(x--,y--)每次循环判断是否用过,直到到达边界)这样一来又是三个for循环,提高了复杂度。原书作者采用一维数组存放位置信息。                                                                                                                                                         

副对角线
01234567
12345678
23456789
345678910
4567891011
56789101112
678910111213
7891011121314
主对角线
01234567
-10123456
-2-1012345
-3-2-101234
-4-3-2-10123
-5-4-3-2-1012
-6-5-4-3-2-101
-7-6-5-4-3-2-10

 

 

 

 

 

 

 

 

 

其中c[cur]==c[j]就是搜索前面的行有没有用过这一列(cur表示行,c[cur]表示这一行的列元素);cur-c[cur]就是主对角线上的值相等。所以只需要判断cur-c[cur]==j-c[j](j-c[j]表示前面的元素如果c[j]用过的话,那么它所在的对角线就符合上面的主对角线上的值。)同理,副对角线只需要将j+c[j]即可,同样判断是否符合条件。

改进版:

其中递归已经是最简了,那么我们想是否可以简化判断呢?

将判断的for循环去掉,怎么去?上面对角线上的元素是相等的,那么就可以想到将对角线映射到一个一维数组里,比如第一个就是-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,每次只需要判断当前点所在的对角线的一维数组下标是否等于1。一共需要3个判断-两个对角线,一个列。所以使用三个数组存放。

int vis[3][1000]; int n; int tot = 0; int c[100];
void search(int cur) {
	if (cur == n) { 
		tot++; 
		for (int i = 0; i < n; i++)cout << c[i] << " ";//输出可行的排列
		cout << endl;
	}
	else for (int i = 0; i < n; i++) {
		if (!vis[0][i] && !vis[1][cur + i] && !vis[2][cur - i + n]) { //cur-i+n之所以要加n,是因为下标可能为负
			c[cur] = i;
			vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 1;//将这个点的列全置为1,对角线置为1
			search(cur + 1);//搜索下一层
			vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 0;//循环搜索第二种情况时需要将列和对角线清除
		}
	}
}

数学方法简单的解决了8皇后问题,主要就是简化判断。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值