Codeforces Round #658 (Div. 2) D. Unmerge

D. Unmerge

题目链接-D. Unmerge
Codeforces Round #658 (Div. 2) D. Unmerge_第1张图片
Codeforces Round #658 (Div. 2) D. Unmerge_第2张图片
题目大意
定义两个数组的合并 m e r g e ( a , b ) merge(a,b) merge(ab),每次将数组 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[lr]),那么 a [ l … r ] a[l…r] a[lr]在序列 s s s中一定是连续的, a [ i ] > m a x ( b [ l … r ] ) a[i]>max(b[l…r]) a[i]>max(b[lr])同理,所以我们可以将这个序列进行分段,也就是说 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
#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;
}

你可能感兴趣的:(codeforces,背包问题)