【Codeforces】#658 D.Unmerge DP

D.Unmerge

题意

定义 (merge(a,b))
a,b是两个长度均为 n 的数组.
如果 (a_1 < b_1) (merge(a,b)) = (a_1) + (merge(a_2+ a_3 + a_4 ... , b))
如果 (a_1 > b_1) (merge(a,b)) = (b_1) + (merge( a , b_2+b_3+b_4+...))
给出一个正整数 n ,以及长度为 2 * n 的全排列,问这个全排列是否可能是两个长度为 n 的数组merge 得到

思路

写一下就会发现:
如果第 i 个数字是 x ,第一个下标大于 i ,并且 (p_j > x)(p_j) 表示下标为 j 的数字) 的 j
这 j - i 个数字应该来自一个数组。
那么我们统计应该来自一个数组的数字个数,进行 DP ,判断能否形成长度为 n 的数组即可。

代码

/*
 * @Autor: valk
 * @Date: 2020-07-17 16:50:40
 * @LastEditTime: 2020-08-17 11:34:55
 * @Description: 如果邪恶 是 华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
 */

#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;

int arr[N], brr[N], dp[N];
int main()
{
    int _;
    scanf("%d", &_);
    while (_--) {
        int n, cnt = 0;
        scanf("%d", &n);
        n *= 2;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
        }
        for (int i = 1; i <= n;) {
            int j = i;
            while (j <= n && arr[j] <= arr[i])
                j++;
            brr[++cnt] = j - i;
            i = j;
        }
        memset(dp, 0x8f, sizeof(dp));
        dp[0] = 1;
        for (int i = 1; i <= cnt; i++) {
            for (int j = 2000; j >= brr[i]; j--) {
                dp[j] = max(dp[j], dp[j - brr[i]]);
            }
        }
        if (dp[n / 2] == 1) {
            printf("YES
");
        } else {
            printf("NO
");
        }
    }
    // fuck;
    return 0;
}
原文地址:https://www.cnblogs.com/valk3/p/13518982.html