互不侵犯
【题目描述】
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
【输入描述】
只有一行,包含两个数N、K(1 <= N <= 9,0 <= K <= N*N)。
【输出描述】
输出一行,包含一个整数,表示方案数。
【样例输入】
3 2
【样例输出】
16
50分暴搜(暴力都不会写,我真是弱得无人能及!):
源代码: #include<cstdio> int n,k,ans(0),x[8]={1,1,1,0,0,-1,-1,-1},y[8]={-1,0,1,1,-1,-1,0,1}; bool f[10][10]; void DFS(int X,int Y,int S) //如下图,一行接一行地搜索,避免重复,值得借鉴。 { if (S==k) { ans++; return; } if ((n-X)*n+(n-Y)<=(k-S)) //A*优化。 return; for (int a=X;a<=n;a++) for (int b=1;b<=n;b++) if (!f[a][b]&&(a>X||b>Y)) { bool t(0); for (int c=0;c<8;c++) if (f[a+x[c]][b+y[c]]) //值得借鉴,周围有国王就不成立。 { t=true; break; } if (!t) { f[a][b]=true; DFS(a,b,S+1); f[a][b]=false; } } } int main() //Re:从制杖开始的沙茶生活。 { scanf("%d%d",&n,&k); for(int a=1;a<=n;a++) for(int b=1;b<=n;b++) { f[a][b]=true; DFS(a,b,1); f[a][b]=false; } printf("%d",ans); return 0; } /* ***** ***** **#.. ..... ..... 设当前点为"#",则搜索区域为"."。 */
状态压缩动态规划(正解):
源代码: #include<cstdio> long long f[10][101][512],Ans; //注意数据类型。 int M,N,K,Stay[101],Num[101]; //Stay[]记录每种状态压缩后的值,Num[]记录对应的状态中1的个数。 bool Map[101][101]; void DFS(int k,int Place,int Now) //k是放了几颗,Place是当前放的位置,Now是当前状态压缩后的值。 { Stay[++M]=Now; Num[M]=k; if (k>=(N+1)/2||k>=K) //边界剪枝。 return; for (int a=Place+2;a<=N;a++) //国王不相邻。 DFS(k+1,a,Now+(1<<(a-1))); //注意,状态是颠倒的。 } void Init() { DFS(0,-1,0); //预处理出一行的每种状态,共有M种。 for (int a=1;a<=M;a++) for (int b=1;b<=M;b++) Map[a][b]=Map[b][a]=((Stay[a]&Stay[b])||((Stay[a]>>1)&Stay[b])||((Stay[a]<<1)&Stay[b]))?0:1; //预处理状态共存情况。 for (int a=1;a<=M;a++) f[1][Num[a]][a]=1; //预处理边界。 } int main() //状态压缩DP。 { scanf("%d%d",&N,&K); Init(); for (int a=2;a<=N;a++) //行数,1为边界,从2开始转移。 for (int b=0;b<=K;b++) //棋子数。 for (int Now=1;Now<=M;Now++) //这一行的情况。 { if (Num[Now]>b) //大于所给棋子数,跳过。 continue; for (int k=1;k<=M;k++) //枚举上一行的状态。 if (Map[k][Now]&&Num[k]+Num[Now]<=b) f[a][b][Now]+=f[a-1][b-Num[Now]][k]; //方案数转移。 } for (int a=1;a<=M;a++) //总数统计。 Ans+=f[N][K][a]; printf("%lld",Ans); return 0; }