D. Unmerge
题目链接-D. Unmerge
题目大意
定义两个数组的合并
m
e
r
g
e
(
a
,
b
)
merge(a,b)
merge(a,b),每次将数组
a
a
a第一个元素和数组
b
b
b第一个元素中最小的那个放到序列
s
s
s中,同时删除那个最小的元素,现在给你一个长度为
2
n
2n
2n的序列,问该序列是否能由两个长度为
n
n
n的数组合并而成
解题思路
分
段
+
01
背
包
分段+01背包
分段+01背包
- 因为每次都是选 a , b a,b a,b中较小的那个放到数组 s s s中,所以我们可以得出如果 b [ i ] > m a x ( a [ l … r ] ) b[i]>max(a[l…r]) b[i]>max(a[l…r]),那么 a [ l … r ] a[l…r] a[l…r]在序列 s s s中一定是连续的, a [ i ] > m a x ( b [ l … r ] ) a[i]>max(b[l…r]) a[i]>max(b[l…r])同理,所以我们可以将这个序列进行分段,也就是说 s s s中分段后的每一段中的元素必定来自同一序列
- 找到
s
i
s_i
si后面第一个比它大的元素进行分段,比如样例中
2 3 1 4
,分段后是2 |3 1| 4
,将每一段的元素个数存入vector
,然后问题就转换为判断是否能将这些段分配给 a , b a,b a,b,使得 a , b a,b a,b大小都为 n n n - 因为每一段只能分配给 a , b a,b a,b中的一个,显然我们可以用 01 背 包 01背包 01背包来实现, d p [ i ] dp[i] dp[i]表示这些段能不能构成大小为 i i i的数组,所以最后判断 d p [ n ] dp[n] dp[n]是否为 1 1 1即可
- 具体操作见代码
附上代码
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x &(-x))
#define endl '\n'
using namespace std;
const int INF=0x3f3f3f3f;
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const double PI=acos(-1.0);
const double e=exp(1.0);
const double eps=1e-10;
const int M=1e9+7;
const int N=4e3+10;
typedef long long ll;
typedef pair<int,int> PII;
typedef unsigned long long ull;
int s[N];
bool dp[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
memset(dp,0,sizeof dp);
for(int i=1;i<=2*n;i++)
cin>>s[i];
int cnt=1,tmp=s[1];
vector<int> v;
for(int i=2;i<=2*n;i++){
if(s[i]>tmp){
v.push_back(cnt);
cnt=1;
tmp=s[i];
}
else cnt++;
}
v.push_back(cnt);
dp[0]=1;
for(int i=0;i<v.size();i++)
for(int j=n;j>=v[i];j--)
if(dp[j-v[i]])
dp[j]=1;
cout<<(dp[n]?"YES":"NO")<<endl;
}
return 0;
}