皇后问题
昨晚, 想实现下N个皇后的问题. 写出思路, 然后代码, 结果调了很久都没有成功. 哪出错了呢.
递归算法的思路如下:
- 从上到下按行逐一进行皇后的放置,所以不用考虑同一行有多个皇后的问题
- 将每一行的皇后位置放置在一个数组当中. 可用于位置判断以及结果输出
- 放置皇后时需有一判断位置是否安全的函数
- 是否同列
- 是否在对角线上
- 递归放置下一行, 若放置到最后一行时, 解数量加一
以下是错误代码.
#include <iostream> #define N 4 intflag[N+1]={0}; bool bSafe(int i, int j); void cq(int i); int count(0); int main() { cq(1); std::cout<<count<<std::endl; system("pause"); } //计算可行组合数 void cq(int i) { for(int j=1; j <= N; j++) if(bSafe(i, j)) { flag[i]=j; if (i < N) cq(++i); else count++; } } //判断指定位置是否安全 bool bSafe(int i, int j) { for (int temp=1; temp<i; temp++) //同列或对角钱上则不安全 if (j == flag[temp] || j-flag[temp] == temp-i || j-flag[temp] == i-temp) return false; return true; }
寻找错误中.... 持续更新中...
两个小时过去了, 终于找到错误了. 有两个地方.
错误一:
第24行:
cq(++i);改为:
cq(i+1);
错误二:
第26行:
count++;改为:
{ count++; break; }
分析:
错误一:
主要是关于++i 与 i+1作为参数时的区别, 其实作为参数传递给函数结果没有什么区别, 只是自己忘记了原来i在本定义域内还有用, 而++却改变了它本身的值. 那么i++呢, 和它的本意一样, 先将自身传给函数再自增. 在VS下测试是如此的, 不知道其它编译器如何.
错误二:
在成功得到一种排列后没有退出循环. 这会造成什么后果使得结果不正确?
奇怪的是, 现在运行时没有break却依然可以得到正确的答案. 凌乱了我的思绪. 若count++后无break,则,在最后一行, 继续在for循环中, 顶多就多进行了N次判断, 按理说,这小于N的判断不会正确, 所以不会产生影响.那么之前的错误又是什么呢? 好吧, 我混乱了, 把上面的代码贴回去,只修改24行得到的结果是正确的. 至少break提高了一点点效率.
稍稍修改过的递归代码:/Files/uglyfly/8queen.rar
以下为非递归算法的探索.
想法: 关于非递归算法, 则必需要有退回上一行的条件. 想的是: 如本行的所以位置都不可用,则退回上一行,从上一行的下一列继续开始判断. 若上一行已经是最后一列了, 遇再退一行.直到行数为0时退出. 如果标识所在行也要引进一个变量.
以下为code:
int findQ(int n) { int now=1;//记录当前所在行, 从第一行开始 int m=0;//总皇后数 while (true) { int j; if (flag[now]==n) now--;//若上一行已经是最后一列了, 遇再退一行 if (now == 0) break;//若已经退到0行, 则说明放置完成 for (j=flag[now]+1; j<=n; j++) {//放置皇后 if(bSafe(now, j)) { flag[now++]=j;//成功则当前行位置输入 flag[now]=0;//下一行从0开始 break; } if (j==n ) now--;//当前行都搜索完毕,返回上一行 } if (now==n+1) { m++; printboard(m); //输出到屏幕 } } return m; }
不足之处欢迎交流^^