趣学算法 n皇后问题
#include<bits/stdc++.h>
using namespace std;
int n;//n表示n个皇后
int x[105];//x[i]表示第i个皇后放置在第i行第x[i]列
int countn;//countn表示n皇后问题可行解的个数
bool check(int t){//约束函数:判断第t个皇后能否放在第i个位置
bool flag=true;
for(int j=1;j<t;++j){//判断该位置的皇后是否与前面t-1个已经放置的皇后冲突
if(x[t]==x[j]||t-j==fabs(x[t]-x[j])){//判断列、对角线是否冲突
flag=false;
break;
}
}
return flag;
}
void dfs(int t){
if(t>n){//如果当前位置为n,则表示已经找到了问题的一个解
countn++;
for(int i=1;i<=n;++i)//打印路径
cout<<x[i]<<" ";
cout<<endl;
cout<<"----------"<<endl;
}
else
for(int i=1;i<=n;++i){
x[t]=i;
if(check(t))
dfs(t+1);
}
}
int main(){
cout<<"输入皇后个数n:";
cin>>n;
countn=0;
dfs(1);
cout<<"答案的个数是:"<<countn<<endl;
return 0;
}
洛谷 八皇后
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
int n;
int x[15];
int cnt;
int cnt2;
bool check(int t){//判断第t个结点能否放在第i个位置
bool flag=1;
for(int i=1;i<t;++i){//<t
if(x[i]==x[t]||t-i==abs(x[i]-x[t])){
flag=0;
break;
}
}
return flag;
}
void dfs(int t){
if(t>n){
++cnt;
if(cnt2<3){
++cnt2;
for(int i=1;i<=n;++i)
printf("%d ",x[i]);
printf("\n");
}
}else {
for(int i=1;i<=n;++i){
x[t]=i;//出现在这里 扩展结点?
if(check(t)) {
dfs(t+1);
}
}
}
}
int main(){
scanf("%d",&n);
dfs(1);
printf("%d\n",cnt);
}
注意:最后一个测试点 n=13 TLE 代码首行手动开启O2优化可以过(真神奇呀)听说用bool标记数组不会超时,等我理解透彻再试
校oj 八皇后
#include<bits/stdc++.h>
using namespace std;
int n=8;
int cnt;
int x[10];
bool check(int t){
bool flag=1;
for(int i=0;i<t;++i){
if(x[t]==x[i]||t-i==abs(x[t]-x[i])){
flag=0;
break;
}
}
return flag;
}
void dfs(int t){
if(t==n){
printf("No. %d\n",++cnt);
for(int j=0;j<n;++j){//按输出要求转置
for(int i=0;i<n;++i){
if(j==x[i])printf("1 ");
else printf("0 ");
}
printf("\n");
}
}else{
for(int i=0;i<n;++i){
x[t]=i;
if(check(t)){
dfs(t+1);
}
}
}
}
int main(){
dfs(0);
}
注:
1.这是第一次从0开始回溯,以前都是从1开始
2.校oj输出顺序要求有点奇怪,通过观察,如果按前两题的板子,转置一下就对了(bright girl)
3.看了别人交的代码,用二维数组搜索,(也有转置的处理(输出book[ j ][ i ])),关键是,它回溯完以后还要清零,表示退回上一层的意思,搞得我有点晕,我的一维标记数组怎么没这种操作?还有他是先check,符合条件再标记,再往下搜,我是先扩展,符合条件再往下一层搜
它确实是要清零才可以呀,因为它是二维图搜索,这一行的这一列搜完,在以后的dfs里,或是因为找到解了,或是因为下面某行找不到满足条件的解,退回到这一层,那此行此列可不得清零吗,for循环继续搜索此行的下一列呀。我的一维数组x[i],记录的是第i行的列数,退回这层时,for循环判断此行下一列,直接x[t]=i了,所以不用清零呀。
行吧,在这自言自语的过程里,已经把那个迷雾重重的代码全弄懂了,贴在下面:
#include<bits/stdc++.h>
using namespace std;
const int n=9;
int book[n][n];
int tot;
bool check(int x,int y){
for(int i=1;i<=x;++i){//判断[1,x]行 是否有与y同列的
if(book[i][y]) return false;
for(int j=1;j<=8;++j){
//二重循环 判断值为1的book[i][j]是否与(正在判断的、即将扩展的)book[x][y]同对角线
if(book[i][j]){
if(x-i==abs(j-y))
return false;
else break;
}
}
}
return true;
}
void dfs(int step){
if(step==n){
++tot;
cout<<"No. "<<tot<<endl;
for(int i=1;i<=8;++i){
for(int j=1;j<=8;++j){
cout<<book[j][i]<<" ";
}
cout<<endl;
}
return;
}
for(int i=1;i<=8;++i){
if(check(step,i)){
book[step][i]=1;//符合条件的话标记为1
dfs(step+1);
book[step][i]=0;
}
}
return;
}
int main(){
dfs(1);
return 0;
}
注:
1.dfs里的那个if,如果满足条件的话最终是要return,所以写不写else都可以
2.关于 满足条件再标记 和 先标记再看是否满足条件 的问题:二维数组解法,满足条件再标记,判断是否满足条件的时候,传过去的是要判断的坐标 (x,y),它只需要坐标,就能判断是否满足条件,而一维数组记录的是列数,必须要先赋值才能判断!我懂啦!(bright girl~)
我发现碎碎念型博客对我思考问题很有帮助(虽然不太好看,但我写爽了就够了2333),帮我条分缕析,直面问题的症结,光用脑袋想的话,我常常忽视这些疑点,囫囵吞枣就过去了,其实自己也知道不是太懂,但没法深入思考——写下来就很好!
八皇后,就到这!
继续做其他回溯的题目!