习题:国王(状压DP)
题目:
B. 国王
内存限制:64 MiB时间限制:500 ms标准输入输出
题目类型:传统评测方式:文本比较
题目描述
原题来自:SGU 223
在 n*n的棋盘上放k 个国王,国王可攻击相邻的 8个格子,求使它们无法互相攻击的方案总数。
输入格式
只有一行,包含两个整数 n 和k 。
输出格式
每组数据一行为方案总数,若不能够放置则输出 0。
样例
样例输入 1
3 2
样例输出 1
16
样例输入 2
4 4
样例输出 2
79
思路:
因为n小,所以第一选择就是搜索或者状压DP,因为每一个各自的状态为放或者不放,总的时间复杂度为O(2^N^2)
之后考虑状压DP ,设dp[i][j][k],为第i行状态为j一共有k个国王,j状态指将j转化成二进制,每一位上的数是1就代表放,
反之则代表不放,
考虑冲突问题
就是上一行的状态右移或者左移再并,
最后统计一下最后一行的答案即可
还有一些小优化,因为每一行的状态的总数是一定的,枚举每一个状态,也就是1~2^n时,
有些状态是不行的,
此时我们将这些可行的状态记录下来
代码
#include<iostream>
using namespace std;
int n,num_king;
long long dp[15][1050][105];
int a[1050];
int num[1050];
int tot;
long long ans;
int main()
{
cin>>n>>num_king;
for(int i=0;i<(1<<n);i++)
{
if(i&(i<<1))
continue;
int t=i;
a[++tot]=i;
while(t)
{
num[tot]+=t%2;
t/=2;
}
}
dp[0][1][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=tot;j++)
{
for(int k=0;k<=num_king;k++)
{
if(k>=num[j])
{
for(int l=1;l<=tot;l++)
{
if(!(a[l]&a[j])&&!(a[l]&(a[j]<<1))&&!(a[l]&(a[j]>>1)))
{
dp[i][j][k]+=dp[i-1][l][k-num[j]];
}
}
}
}
}
}
for(int i=1;i<=tot;i++)
{
ans+=dp[n][i][num_king];
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号