codeforces-#658-D

codeforces-#658-D

七月 22, 2020

C2. Prefix Flip (Hard Version)

题意
给出一个长度为2n的排列p

要求判断能否还原出两个等长的数组a b

使得merge(a,b)=p

定义merge运算:

merge(空集,a)=a

merge(a[1~n] , b[1~n])= a[1]<b[1]?a[1]+merge(a[2~n], b[1~n]) : b[1]+ merge(a[1~n], b[2~n]);

就是比较a和b数组第一个数谁大 就把谁放到新数组(最初为空)的下一个位置

题解

观察样例发现merge运算实际上是当 max(a[l~r]) <b[i] or max(b[l~r]) < a[i]时,

将a[l~r]/ b[l~r] 放在一个b[i]/a[i]前

根据这个性质将2n的排列p打碎成尽量多的子串 假设最后子串有m个

例如3 , 1 , 4 ,5 , 2 , 63. 这个例子被分为(3 , 1) , (4) , (5 , 2) ,(6)

每一个子串属于a/b中的1个 现在可以将问题转化成01背包模型

对于一个容量为n的 背包 m个物品每个 物品体积=价值=这个子串的长度

求能装下物品的前提下最大价值

若最后dp[n]=n说明存在解 否者输出no

参考博客

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
43
44
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
#define endl '\n'
#define Turnoff std::ios::sync_with_stdio(false)

const int mod=2e3;
const int Max=4000+5;
int data[Max],dp[Max];

/*

*/
string s,ns;int n;
int main(){
Turnoff;
int t;cin>>t;
while(t--){
int n;cin>>n;
vector<int>group;int cnt=1;
for(int i=0;i<2*n;i++)cin>>data[i],dp[i]=0;
int maxn=data[0];data[2*n]=2*n+1;
for(int i=1;i<=2*n;i++){
if(data[i]>maxn){
group.push_back(cnt);
cnt=1;
}
else cnt++;
maxn=max(maxn,data[i]);
}
//for(auto i:group)cout<<i<<" ";
//cout<<endl;
int len=group.size();
for(int i=0;i<len;i++){
for(int w=n;w>=group[i];w--){
dp[w]=max(dp[w],dp[w-group[i]]+group[i]);
}
}
//cout<<dp[n]<<endl;
if(dp[n]!=n)cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
}