CF1381B Unmerge(01背包)

挺有意思,主要是仔细观察这个数列和题目所给的信息,CF题目的解法经常就隐藏在题目信息和案例中

首先我们肯定是关注题目所给的merge的定义,发现首位是一个与其他不同的特殊信息。其实不难发现,如果一个数能成为某个小数组的开头来合并的话

在他后面的比他小的数,一定要跟他成为一个集合。否则不成立,因为如果当前作为B开头,那么之后不大于他的肯定不行,并且显然他作为a开头,后面的也不能作为b开头,因为这样无法得到正确答案

因此我们按照这个思想划分,之后做一遍01背包观察是否能组成n就行

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pll;
const int N=2e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int p[N];
int a[N];
int st[N];
int cnt[N];
int f[N];
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        int i;
        for(i=1;i<=2*n;i++){
            st[i]=0;
            f[i]=0;
        }
        for(i=1;i<=2*n;i++){
            cin>>p[i];
        }
        int idx=0;
        int now=2*n;
        int num=2*n;
        int last=2*n;
        while(num){
            int i;
            int tmp=0;
            for(i=1;i<=last;i++){
                if(p[i]==now){
                   tmp=i;
                }
            }
            int res=0;
            for(i=tmp;i<=last;i++){
                res++;
                st[p[i]]=1;
                num--;
            }
            last=tmp-1;
            cnt[++idx]=res;
            while(now&&st[now])
                now--;
        }
        f[0]=1;
        for(i=1;i<=idx;i++){
            for(int j=n;j>=cnt[i];j--){
                f[j]|=f[j-cnt[i]];
            }
        }
        if(f[n]){
            cout<<"YES"<<endl;
        }
        else{
            cout<<"NO"<<endl;
        }
    }
    return 0;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13579714.html