洛谷 2051 [AHOI2009] 中国象棋
这也是一道dp方程很难想出来的dp
要是想通了方程,后续的推导也需要花费一定的时间,所以是一道好题
我直接讲dp方程吧
因为我也没有想出来dp方程,是某个同学告诉我的
dp[i][j][k]表示到了第i行,这一行之前有j列放了1个棋子,有k列放了2个棋子
这个方程确实不好想
转移有一点多
总共有6个转移方程
其实是压了一维,这一维是放了0个棋子的列数
因为这一维可以由其他两维推出他就=m-j-k;
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k])%mod;
表示这一行什么棋子也不取
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k]*(m-j-k+1))%mod;
表示这一行将之前的一个0个全变成1个有多少种方案
就是由j-1在加上了这个变来得1
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+1][k-1]*(j+1))%mod;
表示这一行将之前的一个1全变成2有多少种方案
由j+1减去了1,k-1加上了1
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-2][k]*calc(m-j-k+2))%mod;
这是将之前的2个0全都变成1
因为可以任取2个,所以运用组合数就是C(m-j-k+2,2)
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+2][k-2]*calc(j+2))%mod;
这是将之前的2个1全变成2
通过组合数同样是C(j+2,2)
dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1]*(m-j-k+1)*j)%mod;
这里表示将之前的1个0变成1,一个1变成2
j-1加上当前由0变成的1就是j,再将两个方案乘起来
这就是所有的6个方程
代码如下
1 #include <cstdio> 2 #include <cstdlib> 3 #include <iostream> 4 #include <algorithm> 5 #include <cstring> 6 using namespace std; 7 typedef long long ll; 8 const ll mod=9999973; 9 const ll N=105; 10 ll dp[N][N][N],n,m; 11 ll calc(ll x) 12 { 13 return (x*(x-1)/2)%mod; 14 } 15 int main() 16 { 17 scanf("%lld %lld",&n,&m); 18 dp[0][0][0]=1; 19 for(ll i=1;i<=n;i++) 20 for(ll j=0;j<=m;j++) 21 for(ll k=0;k<=m-j;k++) 22 { 23 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k])%mod;// 1 24 if(m-j-k+1>=0 && j>=1) 25 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-1][k]*(m-j-k+1))%mod;// 2 26 if(j+1>=0 && k>=1) 27 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+1][k-1]*(j+1))%mod;// 3 28 if(m-j-k+2>=0 && j>=2) 29 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j-2][k]*calc(m-j-k+2))%mod;//4 30 if(j+2>=0 && k>=2) 31 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j+2][k-2]*calc(j+2))%mod;// 5 32 if(m-j-k+1>=0 && k>=1 && j>=1) 33 dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k-1]*(m-j-k+1)*j)%mod;// 6 34 } 35 ll ans=0; 36 for(int j=0;j<=m;j++) 37 for(int k=0;k+j<=m;k++) 38 { 39 ans=(ans+dp[n][j][k])%mod; 40 } 41 printf("%lld\n",ans); 42 return 0; 43 }