1087: [SCOI2005]互不侵犯King
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3914 Solved: 2292
[ 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
状压dp,dp[i][j][x]表示第i行状态为x,包括第i行在内已经摆放了j个国王的情况个数
状态仍然用二进制表示,例如n=5时,26对应二进制位11010,就表示第1,2,5个格子上摆了国王
很显然有很多种状态都不合法(同一行国王不相邻),所以可以先搜索预处理出所有的合法行状态
res[x]表示第x个合法状态
cnt[x]表示第x个合法状态共放置了cnt[x]个国王(当然只是那一行)
之后在处理相邻两行的状态
map[i][j]表示上一行是第i个合法状态,下一行是第j个合法状态拼在一起合不合法
最后的最后dp就好了
状态转移:dp[i][j][b] += dp[i-1][j-cnt[b]][c];(前提map[b][c]==1且cnt[b]+cnt[c]<=j)
#include<stdio.h>
#include<string.h>
#define LL long long
LL n, m, k, res[515], cnt[515], jud[515][515], dp[13][83][515];
void Sech(LL id, LL x, LL now)
{
LL i;
res[++m] = now;
cnt[m] = x;
if(x<k)
{
for(i=id+2;i<n;i++)
Sech(i, x+1, now|(1ll<<i));
}
}
int main(void)
{
LL i, j, b, c, ans;
while(scanf("%lld%lld", &n, &k)!=EOF)
{
m = 0;
Sech(-2, 0, 0);
memset(jud, 0, sizeof(jud));
memset(dp, 0, sizeof(dp));
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
if((res[i]&res[j])==0 && ((res[i]<<1)&res[j])==0 && (res[i]&(res[j]<<1))==0)
jud[i][j] = 1;
}
}
for(i=1;i<=m;i++)
dp[1][cnt[i]][i] = 1;
for(i=2;i<=n;i++)
{
for(j=0;j<=k;j++)
{
for(b=1;b<=m;b++)
{
if(cnt[b]>j)
continue;
for(c=1;c<=m;c++)
{
if(jud[c][b]==1 && cnt[b]+cnt[c]<=j)
dp[i][j][b] += dp[i-1][j-cnt[b]][c];
}
}
}
}
ans = 0;
for(i=1;i<=m;i++)
ans += dp[n][k][i];
printf("%lld\n", ans);
}
return 0;
}