ARC068F Solitaire

神仙DP題

首先奉上神仙PuFanyi的博客講解

而後是我這個菜雞的我的理解(推薦上面那篇博客,講的比我好多了)c++


因爲從小到大插入,因此最終序列的兩邊必定要比中間要大,能夠看作一個\(V\)字型序列git

爲了取出\(1\),咱們必定會取完一整個單調的序列和另外一個單調的序列的一部分github

僞裝咱們已經取完了前\(K\)個數,那麼剩下的數是一個單調的序列,選法總數就是\(2^{n-k-1}\),注意當序列只剩一個元素時,隊首和隊尾是等價的優化

考慮前\(K\)個數的選法,能夠DPspa

考慮前\(K\)個數構成了兩個單調遞減的序列,對於肯定的\(K\)個數(順序也是肯定的),只要存在一種方案,使得它可以被合法地加入雙端隊列併合法地取出,那麼該序列合法。故咱們只需一種最有可能合法的方案便可,若該方案合法,說明整個序列都是合法的code

借用PuFanyi的博客中的紅色、藍色和綠色序列的概念,因爲藍色序列的最小值>綠色序列的最大值,因此咱們要儘量把較大的加入藍色序列。這樣最有可能合法隊列

\(f[i,j]\)表示到第i位,最小的一位爲\(j\)的方案數,\(j\)即爲紅色序列末尾get

因此考慮隊首和隊尾,對於較大的一個,即剩下的數中的最大值,若是存在這個大於\(j\)的數,把他放到藍色序列中,不然放入紅色序列中博客

也能夠選擇較小的那一個,若其比\(j\)小,將其放入紅色序列中it

考慮何時存在大於\(j\)的最大值。大於j的數有\(n-j\)個,其中\(i-2\)個已經被選,故\(n-j-i+2>0\)\(n-j+1>=i\),至於紅色序列,任何一個\(<j\)的數都知足要求,由於他必定是全部選的數中最小的,因此沒有不存在的狀況

而後前綴和優化就能夠AC了

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define fo(i,a) for(int i=0;i<a;++i)
#define il inline
#define int long long

const int inf=0x3f3f3f3f,N=2010,mod=1e9+7;

int n,m,dp[N];

il void read(int &x){
    x=0;char c=getchar(),f=1;
    while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

signed main(){
    read(n),read(m);
    dp[n]=1;
    go(i,1,m){
        com(j,n,1){
            if(n-j+1<i) dp[j]=0;
            else (dp[j]+=dp[j+1])%=mod;
        }
    }
    int ans=1;
    go(i,1,n-m-1) ans=ans*2%mod;
    printf("%lld",ans*dp[1]%mod);
    return 0;
}

一份暴力代碼幫助本身理解

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define fo(i,a) for(int i=0;i<a;++i)
#define il inline
#define int long long

const int inf=0x3f3f3f3f,N=2010,mod=1e9+7;

int n,m,dp[N][N];

il void read(int &x){
    x=0;char c=getchar(),f=1;
    while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

signed main(){
    read(n),read(m);
    go(i,1,n) dp[1][i]=1;
    go(i,2,m){
        go(j,1,n){
            if(n-j-i+1>=0) dp[i][j]=dp[i-1][j];
            //檢查當前是否存在合法且最大的數放入藍色序列 
            go(k,j+1,n){
                if(n-k-i+1>=0) (dp[i][j]+=dp[i-1][k])%=mod;
                //檢查dp[i-1][k]是否合法且k是否爲藍色序列的結尾(即只有藍色序列的狀況) 
            }
        }
    }
    int ans=1;
    go(i,1,n-m-1) ans=ans*2%mod;
    printf("%lld",ans*dp[m][1]%mod);
    return 0;
}
相關文章
相關標籤/搜索