1592:【例 1】国王
1592:【例 1】国王
时间限制: 500 ms 内存限制: 65536 KB
提交数: 1169 通过数: 537
【题目描述】
原题来自:SGU 223
在 n×nn×n 的棋盘上放 kk 个国王,国王可攻击相邻的 88 个格子,求使它们无法互相攻击的方案总数。
【输入】
只有一行,包含两个整数 nn 和 kk。
【输出】
每组数据一行为方案总数,若不能够放置则输出 00。
【输入样例】
3 2
【输出样例】
16
【提示】
样例输入 2
4 4
样例输出 2
79
数据范围与提示:
对于全部数据,1≤n≤10,0≤k≤n21≤n≤10,0≤k≤n2 。
思路:
首先考虑搜索,发现可剪枝状态少;接着二维dp,发现状态难以表达
发现国王只影响周围八个格子,也就是说国王只影响这一行的状态和上下两行的状态,可以压缩整行信息,进行状压
f[i][j][s]表示第i行状态为a[j],前i行已经放了s个国王的方案数
考虑如何表示a[](二进制,0表示不放,1表示放)
算法流程:
1.对于每一行,搜索得出一个合法状态
2.枚举上一行与这一行相容的状态累加
代码:
#include<bits/stdc++.h> using namespace std; long long f[11][155][155]={0},ans; int num[155],s[155],n,k,s0; void prepare() { int cnt; s0=0,ans=0; // memset(f,0,sizeof(f)); for(int i=0;i<(1<<n);i++)//枚举每一行的状态,总方案数为2^n { if(i&(i<<1))//检查当前状态是否冲突(当前状态是否存在两个相邻的国王) continue; cnt=0; for(int j=0;j<n;j++) if(i&(1<<j))cnt++;//k表示当前状态用掉的国王数 s[++s0]=i;//最终s数组中存放的是当前行的所有可用状态 num[s0]=cnt; } } void dp() { f[0][1][0]=1; for(int i=1;i<=n;i++)//行 for(int j=1;j<=s0;j++)//状态 for(int kk=0;kk<=k;kk++)//国王数量 { if(kk>=num[j])//若是当前拥有的国王数比这一行需要的国王数多 for(int t=1;t<=s0;t++)//枚举第i-1行的状态 { if(!(s[t]&s[j])&&!(s[t]&(s[j]<<1))&&!(s[t]&(s[j]>>1)))//没有冲突 f[i][j][kk]+=f[i-1][t][kk-num[j]]; } } for(int i=1;i<=s0;i++) ans+=f[n][i][k]; cout<<ans<<'\n'; /*for(int i=1;i<=n;i++) { for(int j=1;j<=s0;j++) { for(int kk=0;kk<=k;kk++) cout<<f[i][j][kk]<<" "; cout<<'\n'; } cout<<'\n'; } cout<<s0<<"@@@@@\n"; cout<<f[3][3][4]<<'\n'; */ /*f[0][1][0]=1;//显然第0行第一位上可以放一个国王 for(int i=1;i<=n;i++) for(int j=1;j<=s0;j++) for(int kk=0;kk<=k;kk++) { if(kk>=num[j])//先看能不能减 for(int t=1;t<=s0;t++) if(!(s[t]&s[j]) && !(s[t]&(s[j]<<1)) && !(s[t]&(s[j]>>1)))//看看国王和上一行的国王起不起冲突 f[i][j][kk]+=f[i-1][t][kk-num[j]]; } for(int i=1;i<=s0;i++) ans+=f[n][i][k];//最后一行的所有状态都要加上 cout<<ans;*/ } int main() { cin>>n>>k; prepare(); dp();//使用函数更方便 return 0; }