骑士
题目描述:
在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的
8个格子,求使它们无法互相攻击的方案总数。
输入格式
共一行,包含两个整数 n和 k。
输出格式
共一行,表示方案总数,若不能够放置则输出0。
数据范围
\(1≤n≤10,\) \(0≤k≤n^2\)
输入样例:
输出样例:
本题就是典型的状态压缩DP。
分为两个部分进行分析:
状态表示
f[i][j][s]
表示所有只摆在前i行,已经摆了j个国王,并且第i行摆放的状态时s的所有方案的集合。
属性
Count(属于计算数量)
经过集合划分(第i排只取决于第i-1排,因此集合划分只需要考虑i-1排的\(2^n\)次方种方案即可)
已经摆完了前i排,并且第i排的状态是a,第i-1排的状态时b,已经摆了j个国王的所有方案实际上等价于->
已经摆完了前i-1排,并且第i-1排的状态时b,已经摆了j-count(a)个国王的所有方案,也就是f[i-1][j-count(a)][b]
思考清楚了转移,接下来看一下要怎么样才能从i-1行到第i行进行一个转移:
只需要满足以下式子即可:
贴上代码
解释详情看注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #include<iostream> #include<vector> using namespace std; const int N=12,M=1<<10,K=110; int n,m; vector<int>state;
vector<int>head[M];
int id[M]; int cnt[M];
int f[N][K][M];
bool check(int state) { for(int i=0;i<n;i++) { if((state>>i&1)&&(state>>i+1&1)) { return false; } } return true; } int count(int state) { int ans=0; for(int i=0;i<n;i++) { ans+=state>>i&1; } return ans; } int main() { cin>>n>>m; for(int i=0;i<1<<n;i++) if(check(i)) { state.push_back(i); id[i]=state.size()-1; cnt[i]=count(i); } for(int i=0;i<state.size();i++) { for(int j=0;j<state.size();j++) { int a=state[i]; int b=state[j]; if((a&b)==0&&check(a|b)) { head[i].push_back(j); } } } f[0][0][0]=1; for(int i=1;i<=n+1;i++) for(int j=0;j<=m;j++) for(int a=0;a<state.size();a++) for(int b:head[a]) { int c=cnt[state[a]]; if(j>=c) { f[i][j][a]+=f[i-1][j-c][b]; } } cout<<f[n+1][m][0]; return 0; }
|