P2051 [AHOI2009]中國象棋(動態規劃)
阿新 • • 發佈:2018-12-15
思路
好像是一道挺水的計數的,不知道為什麼會是紫題
顯然每行和每列最多放兩個
首先考慮狀壓,然後發現三進位制狀壓可做,但是三進位制太麻煩了,可以拆成兩個二進位制,一個表示該列是否是放了一個的,一個表示該列是否是放了兩個的
可以發現並不需要知道具體每列放了什麼,只需要知道有幾個即可,所以把有幾列放了一個和有幾列放了兩個表示進狀態中,滾動陣列優化一下空間即可
狀態轉移在程式碼中
程式碼
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int MOD = 9999973; int m,n,cur; int dp[2][110][110]; int C(int n){ return (1LL*n*(n-1)/2)%MOD; } int main(){ scanf("%d %d",&n,&m); dp[cur][0][0]=1; for(int i=0;i<=n-1;i++,cur^=1){ memset(dp[cur^1],0,sizeof(dp[cur^1])); for(int j=0;j<=m;j++) for(int k=0;k<=m-j;k++){ dp[cur^1][j][k]=(dp[cur][j][k]+dp[cur^1][j][k])%MOD;//不放 if(j+k+1<=m) dp[cur^1][j+1][k]=(dp[cur^1][j+1][k]%MOD+1LL*dp[cur][j][k]*(m-k-j)%MOD)%MOD;//放一個在沒有的列上 if(j>=1) dp[cur^1][j-1][k+1]=(dp[cur^1][j-1][k+1]%MOD+1LL*dp[cur][j][k]*j%MOD)%MOD;//放一個在有一個的列上 if(j+2+k<=m) dp[cur^1][j+2][k]=(dp[cur^1][j+2][k]%MOD+1LL*dp[cur][j][k]*C(m-j-k)%MOD)%MOD;//放兩個在沒有的列上 if(j>=2) dp[cur^1][j-2][k+2]=(dp[cur^1][j-2][k+2]%MOD+1LL*dp[cur][j][k]*C(j)%MOD)%MOD;//放兩個在有一個的列上 if(j+k+1<=m&&j>=1) dp[cur^1][j][k+1]=(dp[cur^1][j][k+1]%MOD+1LL*dp[cur][j][k]%MOD*(m-j-k)%MOD*j%MOD)%MOD;//放一個在沒有的列上,一個在有的列上 } } int ans=0; for(int j=0;j<=m;j++) for(int k=0;k<=m-j;k++){ ans=(ans+dp[cur][j][k])%MOD; } printf("%d\n",ans); return 0; }