回溯算法与八皇后问题

    回溯法:在递归构造中,生成和检查的过程可以有机结合起来,从而减少不必要的枚举。把问题分解为若干个步骤求解时,如果当前步骤没有合法选择,则函数将返回上一级的递归调用,该现象称为回溯法。所以递归枚举通常被称为回溯。

    8皇后问题:在8*8的棋盘上放置了8个皇后,使得他们互不攻击,每个皇后的攻击范围为同行,同列和同对角线。要求找出共有多少种放法。

 分析:最简单的思路是枚举“64个格子的子集”,使得子集中恰有8个格子满足条件。但是枚举64个格子有2^64,显然是个糟糕的模型。第二种思路是“从64个格子中选8个,然后判断是否满足条件“,根据组合数可知这个方案达到10^9数量级,比第一种方案好,但是也不够好。下面用回溯法来解决。用c[x]表示第x行

的皇后的列号,于是问题转变为了8!=40320生成全排列问题。下面给出了回溯法解决8皇后问题的代码:

int c[maxn];
int ans;
void dfs(int curr,int n){   //n皇后问题
	if (curr ==n){ ans++;return; }
	int i, j;
	for (i = 0; i <n; i++){
		c[curr] = i;  //尝试吧curr行的皇后放在第i列
		for (j = 0; j< curr; j++){
			//检查是否与已经放置的皇后冲突
			if (c[curr]== c[j] || c[curr] - curr == c[j] - j || c[curr] + curr == c[j] + j)break;
		}
		//j==i说明上述循环没有提前终止,放在i列是合理的
		if (j == curr) dfs(curr + 1,n);
	}
}

 可以继续提高程序效率。用vis[3][]来标记当前皇后所在的列和对角线是否有其他皇后的攻击,测试了一下,时间大约比上面代码快3倍多int vis[3][maxn];

int vis[3][maxn];
void dfs(int curr,int n){
	if (curr == n){ ans++; return; }
	for (int i = 0; i < n; i++){
		if (!vis[0][i] && !vis[1][curr + i] && !vis[2][curr - i + n]){
			vis[0][i] = vis[1][curr + i] = vis[2][curr - i + n] = 1;  //标记为放
			c[curr] = i;      //无需打印的话可以不使用数组c
			dfs(curr + 1, n);
			vis[0][i] = vis[1][curr + i] = vis[2][curr - i + n] = 0;  //撤销标记
		}
	}
}

  

posted @ 2016-08-30 10:45  曹孟德  阅读(383)  评论(0编辑  收藏  举报