1. 程式人生 > >洛谷 2051 [AHOI2009] 中國象棋

洛谷 2051 [AHOI2009] 中國象棋

name 技術分享 clu 列數 ahoi2009 alc mage 好題 urn

這也是一道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 }

洛谷 2051 [AHOI2009] 中國象棋