本文转载于博客http://blog.csdn.net/bone _ ace/article/details/41419695。 感谢原文作者的无私奉献
问题描述:
8*8的国际象棋棋盘上必须放8位皇后,使任何一位皇后都不能互相吃掉。 规则是皇后可以吃同一行、同一列、同一对角线的棋子。 下图是两种方案。
在、
解决方案:
如果8*8的棋盘不能并排8位皇后,同行排列在同一条对角线上,每行一定会有一位皇后。 可以设定存储每行皇后位置的数组a,元素值可以表示第几列。 例如,a[1]=5表示第一行的queen位于第五个格中。 然后只需求出数组a的值就可以解决问题。 以下是三种回溯解法:
一八个for循环。 在列举方法中,八个for循环分别列举每行的八个位置,但我们无需全部列举完毕,可以采用“剪枝策略”。 也就是说,遇到不合适的情况会追溯。 例如,在a[1]=4的情况下,第2行的a[2]=4与a[1]同列,不符合题意。 接下来的六个周期不需要变穷。 直接’ continue;’ 去检查的a[2]=5.具体代码如下。
void main () { int a[9]; int i,t=1; for(a[1]=1; a[1]9; a[1] ) for(a[2]=1; a[2]9; a[2] () if (! check(a,2 ) )连续; for(a[3]=1; a[3]9; a[3] () if (! check(a,3 ) )连续; for(a[4]=1; a[4]9; a[4] () if (! check(a,4 ) )连续; for(a[5]=1; a[5]9; a[5] () if (! check(a,5 ) )连续; for(a[6]=1; a[6]9; a[6] () if (! check(a,6 ) )连续; for(a )7)=1; a[7]9; a[7] () if (! check(a,7 ) )连续; for(a )8)=1; a[8]9; a[8] () if (! check(a,8 ) )连续; else { printf (第%d种解法((n ),t ); for(I=1; i9; I ) printf ()第%d皇后(%d(n ),I,a[i] ); 打印((n ) n ) ); } } } } } } } } }
/Check函数功能:检查第n行的queen是否与前一个queen发生碰撞,如果没有,则返回1intcheck(inta ),int n ) { for } inti=1; in; I () if ) ABS(a[I]-a[n] )=ABS ) I-n )|| a[i]==a[n] ) /请参阅下面的注return 0。
} return 1; }
代码注释:
某一行的皇后a[n]不能和之前的皇后a[i]位置有冲突,约束条件为:
a、不在同一列:a[n] != a[i]
b、不在同一行:因为现在是每一行求一个皇后的位置,所以同一行不会有冲突,不需要考虑。
c、不在同一对左角线:a[n]-a[i] != n-i
d、不在同一右对角线:a[n]-a[i] != -(n-i)
条件c和d可以合成:abs(a[n]-a[i]) != abs(n-i)
总结:其实这里用到的就是深度优先搜索的思想,从第一行的皇后一直深入去找第二行、第三行…皇后的位置。其中加上了约束条件Check函数进行“剪枝”。这就是回溯算法的思想:深度优先搜索,遇到不满足的情况就进行回溯。
2、方法一的优化。上述代码易读、易懂,但是用八个for循环不免显得很累赘,而且如果要求在100*100的棋盘上放100个皇后这种“N皇后问题“呢?难道用100个for循环吗?我们来把代码优化一下,用到的思想还是和方法一相同:深度优先搜索、回溯。具体代码如下:
void main() { int a[256]={0}; int i=1,j,n,t=1;i表示当前正在搜索第i行的皇后位置 printf(“请输入几皇后?n=”); scanf(“%d”,&n); while(i>0) { for(a[i]++;a[i]<=n;a[i]++) { if(Check(a,i))//如果第i行的皇后与之前的皇后位置上没有冲突,则break跳出循环 break; } if(a[i]<=n)/如果a[i]<=n,即上面的for循环是由“break;”跳出来的,即第i行皇后的位置符合条件 { if(i==n)找到一组解,输出 { printf(“第%d种解法:\n”,t++); for(j=1;j<=n;j++) printf(“第%d个皇后:%d\n”,j,a[j]); printf(“\n\n”); } else未找完 { i++; a[i]=0; } } else i–;回溯 } }
代码注释:上面用到的Check函数和方法一的Check函数相同。
总结:虽然上面代码中只用到两层循环,但是思想、思路和方法一都是一样的,时间复杂度也是和方法一的时间复杂度相同。当n大于10之后运算就已经比较困难了。
3、递归实现。上面两种方法都是用到了深度优先搜索,而一般而言,深度优先搜索都是可以用递归来实现的。下面我们用递归的方式解决八皇后问题。具体代码如下:
int a[20],n,i,t=1;全局变量 void Try(int i) { int j,k; for(j=1;j<=n;j++) { a[i]=j; if(Check(a,i))///如果第j列不会与之前的皇后冲突 { if(i<n)//如果i<n,即还没有找到八个皇后,继续递归 Try(i+1); else 如果找到了一组解就输出 { printf(“第%d种解法:\n”,t++); for(k=1;k<=n;k++) printf(“第%d个皇后:%d\n”,k,a[k]); printf(“\n\n”); } } } } void main() { printf(“几皇后?n=”); scanf(“%d”,&n); Try(1); }
代码注释:
a、此处递归的思路很简单,每一层递归表示一行皇后,j表示列,即a[i]=j表示第i行的皇后位置在第j列。
b、以上用到的Check函数与方法一的Check函数相同。
我不清楚是什么原因,递归的速度竟然明显比前面的两种方法快??
———————————————————————————————————————————————————
以上就是原文的全部内容,本人觉得还是第一种和第二张好理解。
对于递归的那种算法,我还不是很理解,自己需要再想一想。
再次谢谢原作者!
转载于:https://www.cnblogs.com/linkcode/p/8150840.html