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;
}
相关文章
相关标签/搜索