最近学习了一下枚举算法,有两种思路,递归构造和直接枚举。直接枚举的优点就是思路和程序很简洁,缺点就在于无法简便的减少枚举量,必须生成所有的解并进行判断。递归构造就很简单了,在生成枚举量的同时并且可以通过判断减少枚举量从而达到了数量上的减少。简单的说,直接枚举就是先找寻解再判断,递归构造则是先判断再找寻解。人类思维更倾向于先找寻解再判断。
一个八皇后问题可以把回溯算法的精华体现出来。
Q:在棋盘上放置八个皇后,棋盘规格为8*8,使得它们不互相攻击,皇后的攻击范围为同列同行同对角,要求找出所有的解。
假如要使用直接枚举的话,就会有八重循环,可以想象是有多么的庞大。八个for叠加,最后判断语句也难得写。
所以这个问题最好的解决方案则是使用递归构造。最终这个问题则会变成一个全排列生成问题。8的阶层=40320个,最多的枚举量不会超过这个数。
回溯算法的现象是:当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象称为回溯。
示例代码:
void Eight(int cur){
if(cur==n) j++;//如果到了边界则方案+1
else for(int i = 0 ;i <n ;i++){
int ok = 1;
c[cur] = i ;//尝试在cur行i列放入皇后
for(int l = 0 ; l< cur ;l++){
if(c[cur]==c[j]) {//判断是否列相同
ok = 0 ;break;
}
if(cur-c[cur]==j-c[j]){//判断主对角线是否重叠
ok = 0 ;break;
}
if(cur+c[cur]==j+c[j]){//判断副对角线是否重叠
ok = 0 ; break;
}
}
if(ok) Eight(cur+1) ;//如果合法则继续递归
}
}
n为格子数平方根 , cur 传入0
可以看到通过判断减少了Eight函数的调用从而减少了枚举量。