题目链接:https://www.luogu.com.cn/problem/P2051
题意:nxm棋盘放棋子,每行每列都不能放超过2个棋子,求方案数
为什么这题有状压dp的标签......我还一直在想状压怎么写,然而这个n,m<=100。
正解是比较巧妙的dp。设f[i][j][k]表示到了第i行,有j列放了1个棋子,k列放了2个棋子的方案数,那么有转移方程:
f[i][j][k]+=f[i-1][j][k],第i行不放棋子
f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1),在一列没放棋子的放1个
f[i][j][k]+=f[i-1][j+1][k-1]*(j+1),在一列放了1个棋子的放1个,使得这列放了2个棋子
f[i][j][k]+=f[i-1][j-2][k]*C(m-k-j+2,2),在两列没放棋子的各放1个,使得这两列都放了1个
f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2,2),在两列放1个棋子的再各放1个,使得这两列放了2个
f[i][j][k]+=f[i-1][j][k-1]*j*(m-k-j+1),在一列放0个的和一列放1个的,各放一个棋子,使得一列放1个,另外一列放2个
注意每个转移方程的边界条件即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=9999973;
ll f[110][110][110],n,m,i,j,k;
int main(){
cin>>n>>m;
memset(f,0,sizeof(f));
f[0][0][0]=1;
for (i=1;i<=n;i++)
for (j=0;j<=m;j++)
for (k=0;k<=m;k++)
if (j+k<=m&&j+k*2<=i*2){
f[i][j][k]=f[i-1][j][k];
if (j>=1) f[i][j][k]+=f[i-1][j-1][k]*(m-k-j+1);
if (k>=1) f[i][j][k]+=f[i-1][j+1][k-1]*(j+1);
if (j>=2) f[i][j][k]+=f[i-1][j-2][k]*(m-k-j+2)*(m-k-j+1)/2;
if (k>=1) f[i][j][k]+=f[i-1][j][k-1]*(m-j-k+1)*j;
if (k>=2) f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2;
f[i][j][k]%=mod;
}
ll ans=0;
for (i=0;i<=m;i++)
for (j=0;j<=m;j++)
if (i+j<=m&&i+j*2<=2*n) ans=(ans+f[n][i][j])%mod;
cout<<ans<<endl;
return 0;
}