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;
}
posted @ 2018-12-15 16:07  dreagonm  阅读(176)  评论(0编辑  收藏  举报