NCSTOJ 1361 八皇后
在8*8的棋盘上,放置8个皇后,使两两之间互不攻击。满足:
1、不在同一列
2、不在同一行
3、不在同一对角线
输入:
无
输出:
按给定顺序和格式输出所有八皇后问题的解
方案1:1 5 8 6 3 7 2 4
方案2:1 6 8 3 7 4 2 5
…
一个回溯问题:递归函数讲不再递归调用它自己,而是返回上一层调用
主要问题是怎么解决对角线的情况(矩阵下标从0开始,本题要求从1开始,效果一样)
主对角线:
[01234567−10123456−2−1012345−3−2−101234−4−3−2−10123−5−4−3−2−1012−6−5−4−3−2−101−7−6−5−4−3−2−10]一个格子(x,y),其主对角线可以用y−x(x−y效果相同)表示 \left[ \begin{matrix} 0 &1&2&3&4&5&6&7 \\ -1 & 0 &1&2&3&4&5&6 \\ -2&-1&0&1&2&3&4&5\\ -3&-2&-1&0&1&2&3&4\\ -4&-3&-2&-1&0&1&2&3\\ -5&-4&-3&-2&-1&0&1&2\\ -6&-5&-4&-3&-2&-1&0&1\\ -7&-6&-5&-4&-3&-2&-1&0 \end{matrix} \right]\\ 一个格子(x,y),其主对角线可以用y-x(x-y效果相同)表示 ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡0−1−2−3−4−5−6−710−1−2−3−4−5−6210−1−2−3−4−53210−1−2−3−443210−1−2−3543210−1−26543210−176543210⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤一个格子(x,y),其主对角线可以用y−x(x−y效果相同)表示
副对角线:
[0123456712345678234567893456789104567891011567891011126789101112137891011121314]一个格子(x,y),副对角线可以用x+y表示 \left[ \begin{matrix} 0&1&2&3&4&5&6&7\\ 1&2&3&4&5&6&7&8\\ 2&3&4&5&6&7&8&9\\ 3&4&5&6&7&8&9&10\\ 4&5&6&7&8&9&10&11\\ 5&6&7&8&9&10&11&12\\ 6&7&8&9&10&11&12&13\\ 7&8&9&10&11&12&13&14 \end{matrix} \right]\\ 一个格子(x,y),副对角线可以用x+y表示 ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡0123456712345678234567893456789104567891011567891011126789101112137891011121314⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤一个格子(x,y),副对角线可以用x+y表示
#include<cstdio>
int c[15]; //c数组保存皇后存放位置
int cnt=0;
void queen(int cur)
{
if(cur==9) //因为最后还会执行cur+1所以终止条件为9
{
printf("方案%d:",++cnt);
for(int k=1;k<=8;++k)
printf("%d ",c[k]);
printf("\n");
return;
}
for(int i=1;i<=8;++i) //1到8列
{
int ok=1;
c[cur]=i; //放在第cur行第i列
for(int j=1;j<cur;++j)
{
if(c[cur]==c[j]||c[cur]-cur==c[j]-j||cur+c[cur]==j+c[j])//列,主对角线,副对角线
{
ok=0;
break;
}
}
if(ok)
queen(cur+1);
}
}
int main()
{
queen(1);
return 0;
}
改进:
用一个二维数组book[3][20] 来判断当前位置的列对角线是否存在其他的皇后
book[0][i] 代表第i列有没有其他皇后
book[1][pos+i] 代表当前副对角线有没有其他皇后
book[2][pos-i+8] 代表当前主对角线有没有其他皇后(其中加8是因为pos-i可能为负)
数组中的第一维0,1,2并不代表行或列,只是表明0存放列数据,1存放副对角线数据,2存放主对角线数据
#include<cstdio>
int cnt=0,c[15],book[3][20]; //c数组保存皇后存放位置
void queen(int pos)
{
if(pos==9) //因为最后还会执行cur+1所以终止条件为9
{
printf("方案%d:",++cnt);
for(int k=1;k<=8;++k) //存放位置,k代表行,c[k]代表列
printf("%d ",c[k]);
printf("\n");
return;
}
for(int i=1;i<=8;++i)
{
if(!book[0][i]&&!book[1][pos+i]&&!book[2][pos-i+8]) //列,副对角线,主对角线
{
c[pos]=i;
book[0][i]=book[1][pos+i]=book[2][pos-i+8]=1; //标记该位置的列,对角线不可用
queen(pos+1);
book[0][i]=book[1][pos+i]=book[2][pos-i+8]=0; //回溯完成,返回上一层调用,回溯前的皇后被拿走,要求恢复原状态
}
}
}
int main()
{
queen(1);
return 0;
}
运行完成后有92种可行方案