BZOJ1801 [Ahoi2009]chess 中国象棋 动态规划
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1801
题意概括
在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.
n,m<=100
题解
其实就是不出现3炮共线就可以了。
用dp[i][j][k]表示前i行,有j列还可以放1个跑,有k列还可以放2个跑的方案总数。
然后对于每一行,分别按照放0、1、2个炮进行转移。
放0个跑,那么就是:
dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;
放1个,可以放在还可以放1个炮的列中,也可以放在还可以放2个炮的列中:
if (j>=1) dp[i+1][j-1][k]=(dp[i+1][j-1][k]+dp[i][j][k]*j)%mod;
if (k>=1) dp[i+1][j+1][k-1]=(dp[i+1][j+1][k-1]+dp[i][j][k]*k)%mod;
放2个,可以都放在还可以放1个炮的列中,也可以都放在还可以放2个炮的列中,也可以每边放一个:
if (j>=2) dp[i+1][j-2][k]=(dp[i+1][j-2][k]+dp[i][j][k]*(j*(j-1)/2))%mod;
if (k>=2) dp[i+1][j+2][k-2]=(dp[i+1][j+2][k-2]+dp[i][j][k]*(k*(k-1)/2))%mod;
if (j>=1&&k>=1)dp[i+1][j][k-1]=(dp[i+1][j][k-1]+dp[i][j][k]*j*k)%mod;
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int N=100+5; const LL mod=9999973; int n,m; LL dp[N][N][N]; int main(){ scanf("%d%d",&n,&m); memset(dp,0,sizeof dp); dp[0][0][m]=1; for (int i=0;i<n;i++) for (int j=0;j<=m;j++) for (int k=0;k+j<=m;k++){ if (!dp[i][j][k]) continue; dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod; if (j>=1) dp[i+1][j-1][k]=(dp[i+1][j-1][k]+dp[i][j][k]*j)%mod; if (k>=1) dp[i+1][j+1][k-1]=(dp[i+1][j+1][k-1]+dp[i][j][k]*k)%mod; if (j>=2) dp[i+1][j-2][k]=(dp[i+1][j-2][k]+dp[i][j][k]*(j*(j-1)/2))%mod; if (k>=2) dp[i+1][j+2][k-2]=(dp[i+1][j+2][k-2]+dp[i][j][k]*(k*(k-1)/2))%mod; if (j>=1&&k>=1) dp[i+1][j][k-1]=(dp[i+1][j][k-1]+dp[i][j][k]*j*k)%mod; } LL ans=0; for (int i=0;i<=m;i++) for (int j=0;j<=m;j++) ans=(ans+dp[n][i][j])%mod; printf("%lld",ans); return 0; }