传送门
题意:
定义两个数组合并:每次比较开头的两个数,取小的数放入新数组,然后删除,重复上述过程,最后形成一个新数组。
现在给出这个新数组,为全排列,求是否存在两个长度为
n
n
n的数组合并后为这个新数组。
题解:
首先我们要发现,一个数
x
x
x 如果被放入新数组里,那么说明有一个数比他大,且这个数后面会被放到数组里。
那么我们可以在新数组对
x
x
x 找到第一个大于它的数
y
y
y ,那么这两者之间的数就比
x
x
x 要小,那么这些数就不可能跟
x
x
x 在不同的数组里,否则先进来的就不会是
x
x
x 。那么就可以知道,这些数一定跟
x
x
x 在同一个数组里,且是连着的。
找出所有所有这个连续段后,问题就转换成了背包问题,即在这些段中找出一些段,使其长度相加刚好为
n
n
n?? 。
代码:
#pragma GCC diagnostic error "-std=c++11"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7;
const int MAXN = 4e3 + 5;
const int inf = 0x3f3f3f3f;
int p[MAXN];
vector<int> v;
int dp[MAXN];
int main()
{
int t;
cin >> t;
while (t--) {
v.clear();
int n;
cin >> n;
for (int i = 1; i <= 2 * n; i++) {
cin >> p[i];
dp[i] = 0;
}
for (int i = 1; i <= 2 * n; i++) {
int cnt = 0;
int j;
for (j = i; j <= 2 * n; j++) {
if (p[j]<=p[i])
cnt++;
else
break;
}
v.push_back(cnt);
i = j - 1;
}
dp[0] = 1;
for (auto i : v) {
for (int j = n; j >= i; j--) {
dp[j] |= dp[j - i];
}
}
if (dp[n]) {
cout << "YES" << endl;
} else
cout << "NO" << endl;
}
}
|