1. 程式人生 > >P2051 [AHOI2009]中國象棋(動態規劃)

P2051 [AHOI2009]中國象棋(動態規劃)

思路

好像是一道挺水的計數的,不知道為什麼會是紫題

顯然每行和每列最多放兩個

首先考慮狀壓,然後發現三進位制狀壓可做,但是三進位制太麻煩了,可以拆成兩個二進位制,一個表示該列是否是放了一個的,一個表示該列是否是放了兩個的

可以發現並不需要知道具體每列放了什麼,只需要知道有幾個即可,所以把有幾列放了一個和有幾列放了兩個表示進狀態中,滾動陣列優化一下空間即可

狀態轉移在程式碼中

程式碼

#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;
}