Codeforces Round #658 (Div. 2) D. Unmerge(dp)
阿新 • • 發佈:2020-07-22
題目連結:https://codeforces.com/contest/1382/problem/D
題意
給出一個大小為 $2n$ 的排列,判斷能否找到兩個長為 $n$ 的子序列,使得二者歸併排序後能夠得到該排列。
題解
將原排列拆分為一個個連續子序列,每次從大於上一子序列首部的元素處分出下一連續子序列,只要將這些子序列按照拆分先後排列,歸併排序後一定可以得到原排列。
之後即判斷能否將這些子序列排列為兩個長為 $n$ 的序列即可,可以用狀壓 $dp$,也可以用 $01$ 揹包。
狀態 $dp$:每次將之前的每一個可行長度加上當前長度得到新一批的可行長度,然後將當前長度標記為可行。
$01$ 揹包:將每個子序列的長度視為其花費與價值,最後判斷花費為 $n$ 的揹包總價值是否為 $n$ 即可。
程式碼一
狀壓 $dp$:$O_{(n)}$
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n; cin >> n;
int mx = 0;
vector<int> idx;
for (int i = 0; i < 2 * n; ++i) {
int x; cin >> x;
if (x > mx) {
mx = x;
idx.push_back(i);
}
}
idx.push_back(2 * n);
vector<int> len;
for (int i = 1; i < idx.size(); ++i) {
len.push_back(idx[i] - idx[i - 1]);
}
bitset<2020> dp;
for (auto i : len) {
dp |= dp << i;
dp[i] = 1;
}
cout << (dp[n] ? "YES" : "NO") << "\n";
}
int main() {
int t; cin >> t;
while (t--) solve();
}
程式碼二
$01$ 揹包:$O_{(vn)}$
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n; cin >> n;
int mx = 0;
vector<int> idx;
for (int i = 0; i < 2 * n; ++i) {
int x; cin >> x;
if (x > mx) {
mx = x;
idx.push_back(i);
}
}
idx.push_back(2 * n);
int N = idx.size() - 1, p = 0;
int cost[N] = {}, val[N] = {};
for (int i = 1; i < idx.size(); ++i) {
cost[p] = val[p] = idx[i] - idx[i - 1];
++p;
}
map<int, int> dp;
for (int i = 0; i < N; ++i) {
for (int j = n; j >= cost[i]; --j) {
dp[j] = max(dp[j], dp[j - cost[i]] + val[i]);
}
}
cout << (dp[n] == n ? "YES" : "NO") << "\n";
}
int main() {
int t; cin >> t;
while (t--) solve();
}