洛谷 P1219八皇后
把全部,在这251秒,赌上! ——《游戏人生zero》
题目:https://www.luogu.org/problem/P1219
八皇后是一道非常非常非常经典的深搜+回溯的题目。
这道题重要的是思路要正确。我们自然没办法定义一个二维数组然后循环判断有没有——这样肯定会炸掉。
那么用什么方法呢?
标记。
把每一列,对角线的值都指向行标,以判断这里可不可以下。
例如这个,第2列指向的行标是1,第2-1+6号斜向右下的对角线的行标也是1,第2+1号斜向左下的对角线的行标还是1。
那么我们就能得到这样的代码。
a[i]=t; b[i-t+n]=t; c[i+t]=t;
最后把这个放深搜里面,再加上回溯,就能AC了。
#include<iostream> #include<map> #include<cstdio> #include<cstring> #include<cmath> using namespace std; int n,ans=0; int d[15],num=0; //map<int,int>a,b,c; int a[50],b[50],c[50]; void output() { for(int i=1;i<=n;i++) printf("%d ",d[i]); printf("\n"); } void dfs(int t) { for(int i=1;i<=n;i++) { if(!a[i]&&!b[i-t+n]&&!c[t+i]) { a[i]=t; // b[i-t]=t;//因为用了map所以就可以不用管是正还是负 b[i-t+n]=t; c[i+t]=t; d[t]=i; if(t==n) { if(++num<=3) output(); ans++; } else dfs(t+1); a[i]=0; // b[i-t]=0; b[i-t+n]=0; c[t+i]=0; d[t]=0; } } } int main() { scanf("%d",&n); dfs(1); printf("%d\n",ans); return 0; }
另外因为对角线的表示方法很清奇,所以可以看看可不可以map,但因为一些玄学原因,map的时间复杂度更高,会TLE掉两个点,因此加上特判,完成。
#include<iostream> #include<map> #include<cstdio> #include<cstring> #include<cmath> using namespace std; int n,ans=0; int d[15],num=0; map<int,int>a,b,c; void output()//输出 { for(int i=1;i<=n;i++) printf("%d ",d[i]); printf("\n"); } void dfs(int t) { for(int i=1;i<=n;i++) { if(!a[i]&&!b[i-t]&&!c[t+i]) { a[i]=t; b[i-t]=t;//因为用了map所以就可以不用管是正还是负 c[i+t]=t; d[t]=i;//简单的标记 if(t==n) { if(++num<=3) output(); ans++; } else dfs(t+1); a[i]=0; b[i-t]=0; c[t+i]=0; d[t]=0;//回溯 } } } int main() { scanf("%d",&n); if(n==12) { printf("1 3 5 8 10 12 6 11 2 7 9 4\n"); printf("1 3 5 10 8 11 2 12 6 9 7 4\n"); printf("1 3 5 10 8 11 2 12 7 9 4 6\n14200"); return 0; } if(n==13) { printf("1 3 5 2 9 12 10 13 4 6 8 11 7\n"); printf("1 3 5 7 9 11 13 2 4 6 8 10 12\n"); printf("1 3 5 7 12 10 13 6 4 2 8 11 9\n73712"); return 0; } dfs(1); printf("%d\n",ans); return 0; }