一般状压dp的问题是求合理的方案,无数量限制。而此题加入了一个K,所以相应地设表示第i行摆k个状态为S的可行方案。
此题又没有障碍物之类,所以只需求一次可行的状态, 只需判断相邻是否可攻击到即可。
然后在上下行转移时,需要考虑到四个角也不能攻击到,上下行国王的左右间隔至少为1,但是靠左的国王可能在上一行也可能在下一行所以要分别判断。
状态转移方程: 。其中S,T需要满足上述关系。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int N,K,state[(1<<13)],num[(1<<13)],cnt=0;
LL f[12][100][(1<<13)]; //注意 long long
void getstate()
{
cnt=0;
int S,Num,i,flag;
for(S=0;S<(1<<N);S++)
{
Num=flag=0;
for(i=0;i<N;i++)
{
if((S&(1<<i))&&(S&(1<<(i+1)))) flag=1; //不能互相攻击到
if(flag) break;
if((S&(1<<i))) Num++;
}
if(flag) continue;
if(Num>K) continue;
state[++cnt]=S;
num[cnt]=Num;
}
}
int main()
{
cin>>N>>K;
int i,j,j_up,j_down;
getstate(); //求一次可行状态
for(j_up=1;j_up<=cnt;j_up++)
f[1][num[j_up]][j_up]=1; //初始化第一列
for(i=1;i<N;i++)
{
for(j_down=1;j_down<=cnt;j_down++) //枚举第i列的状态
for(j=0;j<=K;j++) //枚举第i+1列的个数
{
if(num[j_down]>j) continue;
for(j_up=1;j_up<=cnt;j_up++) //枚举第i+1列的状态
{
if((state[j_down]&state[j_up])) continue; //三个判断条件
if(((state[j_down]<<1)&state[j_up])) continue;
if(((state[j_down]>>1)&state[j_up])) continue;
f[i+1][j][j_down]+=f[i][j-num[j_down]][j_up];
}
}
}
LL ans=0;
for(j_up=1;j_up<=cnt;j_up++)
ans+=f[N][K][j_up];
cout<<ans;
return 0;
}