题目链接: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; }