CF1381B Unmerge 题解

本文迁移自洛谷原文

结论+dp

我们可以先观察样例。当 $n=4$ 时,整个序列的最大值为8。在merge后的序列中8后面的数在原序列中一定跟在8后面。同理得,在7后面8前面的所有数都一定跟在7后面。

于是原序列被分成了很多小段。现在要做的事就是将这些小段重新组合,得到两个长度为 $n$ 的原序列。由于你已经分好了,所以你不需要知道小段内的内容,只要知道它们的长度,然后01背包即可。

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<bits/stdc++.h>
using namespace std;
const int mxn=4002;
vector<int>g[mxn];
int n,m;
int a[mxn],pos[mxn];
bool ban[mxn];
vector<int>v;
int dp[mxn][mxn];
inline void solve(){
cin>>n;for(int i=1;i<=n*2;++i)pos[i]=0;//千万不能用memset!会TLE!
for(int i=1;i<=n*2;++i)ban[i]=0;
v.clear();
for(int i=1;i<=n*2;++i)cin>>a[i],pos[a[i]]=i;
for(int i=n*2;i;--i){ //分割
int cnt=0;
for(int j=pos[i];j<=n*2 and !ban[j];)++cnt,ban[j]=1,++j;
if(cnt)v.push_back(cnt);
}
for(int i=0;i<=v.size();++i)for(int j=0;j<=n;++j)dp[i][j]=0;
dp[0][0]=1;
sort(v.begin(),v.end());
for(int i=0;i<v.size();++i){ //背包
for(int j=0;j<=n;++j){
if(dp[i][j]==1){
dp[i+1][j]=1;
if(j+v[i]<=n){
dp[i+1][j+v[i]]=1;
}
}
}
}
if(dp[(int)(v.size())][n])cout<<"YES\n";
else cout<<"NO\n";
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T=1;cin>>T;
for(;T--;)solve();
}