DP百题进度:8/100
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1087
1087: [SCOI2005]互不侵犯King
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4488 Solved: 2599
[ Submit][ Status][ Discuss]
Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
HINT
一道十分经典的状态压缩dp题,题解应该遍地都是吧_(:з」∠)_但是鉴于本渣状压dp实在是太弱了,所以还是写一篇加深印象吧。
首先能想到dp[i][j]表示前i行放了j个国王的方案数,但很显然我们无法维护上一行对当前行的影响,所以要加一维二进制数state来维护上一行的状态。
通过枚举第一行的状态和当前行的状态,我们可以推出转移方程:dp[i][j][state]+=dp[i-1][j-num][pre];
然后就很好写了,注意判断状态不合法的情况。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int read()
{
char c;int sum=0,f=1;c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,k;
LL dp[10][90][1<<10],ans;
int count1(int x)
{
int ans=0;
while(x)
{
if(x&1)
ans++;
x>>=1;
}
return ans;
}
bool check(LL x,LL y)
{
if(x&(y<<1))
return false;
if(x&(y>>1))
return false;
if(x&y)
return false;
return true;
}
void DP()
{
dp[0][0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++)
for(int state=0;state<=(1<<n)-1;state++)
{
int num=count1(state);
if(num>j) continue;
if(state&(state>>1)) continue;
for(int pre=0;pre<=(1<<n)-1;pre++)
{
if(!check(pre,state)) continue;
if(pre&(pre>>1)) continue;
dp[i][j][state]+=dp[i-1][j-num][pre];
}
}
}
int main()
{
n=read();k=read();
DP();
for(int state=0;state<=(1<<n)-1;state++)
ans+=dp[n][k][state];
cout<<ans<<endl;
return 0;
}