暴力搜索------回溯法

       回溯法(backtracking)是深度优先搜索(DFS)的一种,按照深度优先的顺序便利解答树。应用范围很广,只要能把待求解的问题分成不太多的步骤,每个步骤又只有不太多的选择,都可以考虑应用回溯法。在学习回溯法之前,一定要保证递归程序能熟练准确地写出。

       当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择则函数将返回上一级递归调用。

例如:

       八皇后问题:在棋盘上放置八个皇后,使得它们互不攻击,每个皇后的攻击范围是同行同列同对角线,找出所有解的个数。

       如果把问题相成从64个格子中选8个格子,那么根据组合数学需要4.426*10^9种方案,但是如果用数组C[x]表示第x行皇后的列编号,则问题变成了全排列生成问题。而0~7的全排列一共有8!=40320个,枚举量不会超过它 。

       下面以四皇后问题阐述具体思路:

第一行,皇后可以在第一列、第二列、第三列、第四列。用a[i][j]来表示皇后可能放置的位置,如图

暴力搜索------回溯法_第1张图片

假设第一行的皇后在a[0][0],那么在第二行皇后可能放置的合法位置如图

暴力搜索------回溯法_第2张图片

假设第二行皇后放在a[1][2],那么第三行无法放皇后,所以回溯到第一行的a[0][0],走下一条路a[1][3],如图

暴力搜索------回溯法_第3张图片

第四行无法再放置皇后,说明第一个皇后不能在a[0][0],回溯到最开始,让皇后放置在a[0][1],依此类推得到解答树。

       观察解答树,改变一次行,遍历四个列(a[1][0]和a[1][1]的情况由于不放置皇后所以没有画在图上,但是这两种情况的否定需要遍历判断),所以只需要用一个一维数组C[x]来表示第x行皇后的列编号即可。代码如下:

需要在主函数中读入n(n*n的棋盘),并将解的个数tot清零,然后调用Dfs(0),即可求得解。

void Dfs(int cur)                                //cur表示行 
{
	if(cur == n)                             //递归边界,每走到此得到一个解
		tot++;                           //tot表示解的个数 
	else
	{
		for(int i = 0; i < n; i++)       //i表示列 
		{
			int ok = 1;                  //判断能否放皇后的标志 
			C[cur] = i;                  //尝试把第cur行的皇后放在第i列 
			for(int j = 0; j < cur; j++) //j表示行,通过检查前面几行判断能否放皇后 
			{
		                /*判断列、主对角线、副对角线与前几行皇后是否冲突*/ 
				if(C[cur]==C[j]||cur-C[cur]==j-C[j]||cur+C[cur]==j+C[j])
				{
					ok = 0;
					break;
				} 
			} 
			if(ok)
				Dfs(cur + 1);
		}
	}
} 



C[cur] == C[j] 、cur - C[cur] == j - C[j]、cur + C[cur] == j + C[j]的理解如下:

由于是逐行放置,所以横向一定不会冲突,只需检查是否纵向和斜向冲突即可,在直角坐标系中,y = -x与y = x为正斜向线,类比坐标系,cur为行,抽象为横坐标,C[cur]为列,抽象为纵坐标,由于不一定要通过原点,所以y-x与y+x可以为任意常数,只要这个常数与前一行相等,说明两皇后斜向冲突。




你可能感兴趣的:(算法学习笔记)