• Luogu P2051 [AHOI2009]中国象棋 //DP


    hhy给我推的这道题,我不会,我去康了题解.....

    题解第一个人说自己20min K一道省选DP,还一次AC

    我想着哪个人这么强,往上一翻发现是__stdcall哥哥......也许这就是大佬的世界吧


    50pts:

    首先可以想到的是状压DP,因为一行一列要最多不超过2个棋子,

    所以用3进制表示整个棋盘每列的放了几个,dp[棋盘状态],边界为dp[0]=1

    刷表法比填表法好写DP方程,转移时,在0或者1的位置上加1构成新的棋盘状态,

    最后统计所有棋盘状态就行了。


    100pts:

    在50分的DP中我们反思,什么地方浪费了空间?我们全部状压,存了每条列的顺序和状态

    但是在这道题中顺序对答案不造成影响,于是我们可以只存在这个状态的列有多少条,而忽略顺序

    于是正确的DP状态就来了,dp[摆到x行][有i列摆了1个][有j列摆了2个],

    刷表转移,加法原理转移到下一个状态,乘法原理把当前状态和放法相乘,

    最后统计答案

    刚开始写填表写挂了,然后写刷表,刷表写完了改了填表

    刷表法:

    #include<bits/stdc++.h>
    #define mod 9999973
    
    using namespace std ;
    
    const int MAXN = 110,MAXM = 110;
    long long dp[MAXN][MAXM][MAXM],n,m;
    
    int main(){
        cin>>n>>m;
        dp[0][0][0]=1;
        for(int x=0;x<n;x++){
            for(int i=0;i<=m;i++){
                for(int j=0;i+j<=m;j++){
                    if(dp[x][i][j]){
                        (dp[x+1][i][j] += dp[x][i][j]) %= mod;
                        if(m-i-j>0)(dp[x+1][i+1][j] += dp[x][i][j] * (m-i-j)) %= mod;
                        if(i>=1)(dp[x+1][i-1][j+1] += dp[x][i][j] * i) %= mod;
                        if(m-i-j-1>0)(dp[x+1][i+2][j] += dp[x][i][j] * (m-i-j) * (m-i-j-1) / 2) %= mod;
                        if(m-i-j>=0 && i>=1)(dp[x+1][i][j+1] += dp[x][i][j] * (m-i-j) * i) %= mod;
                        if(i>=2)(dp[x+1][i-2][j+2] += dp[x][i][j] * (i-1) * i / 2) %= mod;
                    }
                }
            }
        }
        long long ans = 0;
        for(int i=0;i<=m;i++){
            for(int j=0;j<=m;j++){
                if(i+j<=m){
                    ans += dp[n][i][j];
                    ans %= mod;
                }
            }
        }
        cout<<ans<<endl;
        return 0;
    }

    填表法:

    #include<bits/stdc++.h>
    #define mod 9999973
    
    using namespace std ;
    
    const int MAXN = 110,MAXM = 110;
    long long dp[MAXN][MAXM][MAXM],n,m;
    
    int main(){
        cin>>n>>m;
        dp[0][0][0]=1;
        for(int x=1;x<=n;x++){
            for(int i=0;i<=m;i++){
                for(int j=0;i+j<=m;j++){
                    dp[x][i][j] += dp[x-1][i][j],dp[x][i][j] %= mod;//什么也不做 
                    if(i>=1) dp[x][i][j] += dp[x-1][i-1][j] * (m-i-j+1),dp[x][i][j] %= mod;//放一个在一列0上;
                    if(j>=1) dp[x][i][j] += dp[x-1][i+1][j-1] * (i+1),dp[x][i][j] %= mod;//放一个在一列1上;
                    if(i>=2) dp[x][i][j] += dp[x-1][i-2][j] * (m-i-j+2) * (m-i-j+1) / 2,dp[x][i][j] %= mod;//放两个在2列0上
                    if(j>=2) dp[x][i][j] += dp[x-1][i+2][j-2] * (i+2) * (i+1) / 2,dp[x][i][j] %= mod;//放两个在2列1上 
                    if(j>=1 && i>=1) dp[x][i][j] += dp[x-1][i][j-1] * (m-i-j+1) * i,dp[x][i][j] %= mod;//放2个,一个在1,一个在2上 
                }
            }
        }
        long long ans = 0;
        for(int i=0;i<=m;i++){
            for(int j=0;i+j<=m;j++){
                ans += dp[n][i][j];
                ans %= mod;
            }
        }
        cout<<ans<<endl;
        return 0;
    }

     状态设计是一个巧活,状态是获得答案的最简条件时,这个DP才是最好的。

    TAG:SIN_XIII ⑨

  • 相关阅读:
    我向老师问声好
    创业的27个真相:破釜沉舟时,要先学会潜水
    印象品牌:成功的标志
    稻盛和夫:打造两个世界500强的强者思维
    假如我们不曾相逢
    让我的诗句带走你的空虚
    昨日的誓言
    假如我有一百万
    我把我的青春献给你
    代腾飞(一读者为IT诗人代腾飞名字作诗)
  • 原文地址:https://www.cnblogs.com/SINXIII/p/11235592.html
Copyright © 2020-2023  润新知