zoukankan      html  css  js  c++  java
  • 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 ⑨

  • 相关阅读:
    LeetCode15 3Sum
    LeetCode10 Regular Expression Matching
    LeetCode20 Valid Parentheses
    LeetCode21 Merge Two Sorted Lists
    LeetCode13 Roman to Integer
    LeetCode12 Integer to Roman
    LeetCode11 Container With Most Water
    LeetCode19 Remove Nth Node From End of List
    LeetCode14 Longest Common Prefix
    LeetCode9 Palindrome Number
  • 原文地址:https://www.cnblogs.com/SINXIII/p/11235592.html
Copyright © 2011-2022 走看看