【[AHOI2009]中國象棋】
阿新 • • 發佈:2019-01-01
計數類dp還是要多寫啊
看上去並沒有什麼思路,加上被題解裡狀壓的標籤迷惑了,於是就去看了一眼題解裡設計的狀態
之後就很好做了
首先先搞明白這道題的本質,就是對於任何一行任何一列炮的個數都不能超過\(2\)
我們設\(dp[i][j][k]\)表示到了第\(i\)行一共有\(j\)列的炮個數為\(2\),有\(k\)列個數為\(1\)的總方案數
那麼一個炮都沒有放的列數自然是\(m-k-j\)啦
之後就可以隨便做了
對於每一行我們有三種選擇
不放
放一個
放兩個
之後這就是我們的核心思想了
一共有五種轉移,就是一些簡單的計數原理和組合數學啦
程式碼
#include<iostream> #include<cstring> #include<cstdio> #define re register #define maxn 105 #define LL long long const LL P=9999973; const LL inv=4999987;//2在P意義下的逆元 int n,m; LL dp[maxn][maxn][maxn]; int main() { scanf("%d%d",&n,&m); dp[1][0][0]=1; dp[1][0][1]=m; dp[1][0][2]=m*(m-1)*inv%P; for(re int i=1;i<n;i++) for(re int j=0;j<=m;j++) for(re int k=0;k<=m;k++) { if(j+k>m) continue; int p=m-k-j; if(!dp[i][j][k]) continue; dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%P;//這一行什麼都不放 if(j+1<=m&&k-1>=0) dp[i+1][j+1][k-1]=(dp[i+1][j+1][k-1]+dp[i][j][k]*k%P)%P;//在原來有1個炮的列上放 if(p&&k+1<=m&&j+k+1<=m) dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*p%P)%P;//在原來有0個炮的列上放 if(j+2<=m&&k-2>=0) dp[i+1][j+2][k-2]=(dp[i+1][j+2][k-2]+(dp[i][j][k]*(k-1)*k%P)*inv)%P;//在兩個原來有1的上放 if(p>=2&&k+2<=m&&j+k+2<=m) dp[i+1][j][k+2]=(dp[i+1][j][k+2]+(dp[i][j][k]*(p-1)*p)%P*inv)%P;//在兩個原來有0的上放 if(p&&j+1<=m&&k&&j+1+k<=m) dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*k*p%P)%P;//在一個原來是0,一個原來是1上放 } LL ans=0; for(re int i=0;i<=m;i++) for(re int j=0;j<=m;j++) if(i+j<=m) ans=(ans+dp[n][i][j])%P; std::cout<<ans; return 0; }