来自FallDream的博客,未经允许,请勿转载, 谢谢。
在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.
n,m<=100
直接状压dp不可能了
但是发现所有的列只有放了几个会影响答案,它是哪列无所谓,转移都相同。
所以可以用f[i][j][k]表示前i行有j个列没放,k个列放了1个的状态数,转移的时候计算一下这么转移的方案数即可。
#include<iostream> #include<cstdio> #define MN 100 #define mod 9999973 #define ll long long using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int f[MN+5][MN+5][MN+5],n,m; inline void R(int&x,ll y){x=(1LL*x+y)%mod;} int main() { n=read();m=read(); f[0][m][0]=1; for(int i=1;i<=n;++i) for(int j=0;j<=m;++j) for(int k=0;k<=m;++k) if(f[i-1][j][k]) { if(j>0) R(f[i][j-1][k+1],1LL*j*f[i-1][j][k]); if(j>1) R(f[i][j-2][k+2],1LL*j*(j-1)/2*f[i-1][j][k]); if(k>0) R(f[i][j][k-1],1LL*k*f[i-1][j][k]); if(k>1) R(f[i][j][k-2],1LL*k*(k-1)/2*f[i-1][j][k]); if(j&&k)R(f[i][j-1][k],1LL*j*k*f[i-1][j][k]); R(f[i][j][k],f[i-1][j][k]); } int ans=0; for(int j=0;j<=m;++j) for(int k=0;k<=m;++k) R(ans,f[n][j][k]); printf("%d ",ans); return 0; }