Codeforces Round #658 (Div. 2) D. Unmerge 题解(思维+01背包)

题目链接

题目大意

定义两个数组的合并运算为 每次取出两个数组中第一个元素较小的那一个放入新数组,然后删去那个元素,现给出一个长度为 2 * n 的置换数组,问 能不能找到两个长度为 n 的置换数组合并后为原数组 。

题目思路

emm,居然是背包,我果然是dp菜鸡

首先就是要明白如果在区间[l,r]中如果a[l]为区间最大,那么这一段一定来自于同一个数组(显然)。那么就可以把这个数组拆分,变成一段一段的,然后进行01背包即可

有两种背包方法
第一种
初始化dp[0]=1,看最后dp[n]是否为0

for(int i=1;i<=cnt;i++){
     for(int j=n;j>=v[i];j--){
         dp[j]|=dp[j-v[i]];
     }
 }

第二种
看最后dp[n]是否为n

 for(int i=1;i<=cnt;i++){
         for(int j=n;j>=v[i];j--){
             dp[j] = max (dp[j] , dp[j-v[i]] + v[i]);
         }
     }

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
#define debug printf("I am here\n");
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=2e3+5,mod=998244353,inf=0x3f3f3f3f;
const double eps=1e-10;
int t,n,a[maxn<<1],v[maxn<<1],cnt,dp[maxn<<1],beg=1;
void init(){
    dp[0]=1;
    cnt=0;
    for(int i=1;i<=n;i++){
        dp[i]=0;
    }
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        init();
        int beg=1;
        for(int i=1;i<=2*n;i++){
            scanf("%d",&a[i]);
            if(a[i]>a[beg]){
                v[++cnt]=i-beg;
                beg=i;
            }
        }
        v[++cnt]=2*n-beg+1;//最后一组
        for(int i=1;i<=cnt;i++){
            for(int j=n;j>=v[i];j--){
                dp[j]|=dp[j-v[i]];
            }
        }
        if(dp[n]){
            printf("YES\n");
        }else{
            printf("NO\n");
        }
    }
    return 0;
}

你可能感兴趣的:(dp)