题目链接:[蓝桥杯 2021 省 AB2] 国际象棋 - 洛谷
标签:状态压缩dp
思路:
设置 f[i][a][b][k] 数组 表示 第 i - 1 行状态为 a ,第 i 行状态为b 的方案数 , 最终方案数为所有
f[m][i][j][k]的和。
从1~m行遍历,首先先遍历第i行的前两行的状态 a b,判断 a b 是否冲突,若冲突,则continue;否则,遍历第i行的状态c,判断c是否与 a b 冲突 , 若冲突 ,则continue;否则,计算第i行放置的棋子个数t,f[i][b][c][j]+=f[i-1][a][b][j-t];(即i-2行状态为a,i-1行状态为b,可以转移到第i行状态为c的情况)。
代码:
#include<iostream>
using namespace std;
const int N = 105, M = 1 << 6 , K = 25, MOD = 1e9+7;
int n,m,k;
int ans;
int f[N][M][M][K];
//f[i][a][b][k] 第i-1行状态为a,第i行状态为b,到此有k个棋子的方案数
//计算二进制x有多少个1
int count(int x)
{
int res=0;
while(x)
{
res++;
x-=x & -x;
}
return res;
}
int main()
{
cin>>n>>m>>k;
f[0][0][0][0]=1;
for(int i=1;i<=m;i++)
{
//遍历第i-2行的状态
for(int a=0;a<1<<n;a++)
{
//遍历第i-1行的状态
for(int b=0;b<1<<n;b++)
{
//如果i-1和i-2行不冲突,遍历第i行的状态
if(a & (b<<2) || b & (a<<2)) continue;
for(int c=0;c<1<<n;c++)
{
//判断第i行是否与前两行冲突
if(c&(a<<1) || a&(c<<1)) continue;
if(c&(b<<2) || b&(c<<2)) continue;
//计算第i行有多少个棋子
int t=count(c);
//那么第i行有j个棋子的方案数+=i-1行有j-t个棋子的方案数
for(int j=t;j<=k;++j)
{
f[i][b][c][j]=(f[i][b][c][j]+f[i-1][a][b][j-t])%MOD;
}
}
}
}
}
for(int i=0;i<1<<n;i++)
{
for(int j=0;j<1<<n;j++)
{
ans=(ans+f[m][i][j][k])%MOD;
}
}
cout<<ans<<endl;
return 0;
}