CF1381B Unmerge(位运算的作用)

题目大意:

给定长度为 \(2n\) 的排列 \(p\) 。确定是否存在两个数组 \(a\)\(b\) ,每个数组的长度都为 \(n\) ,并且没有相等的元素,使得 \(p = \operatorname{merge}(a,b)\)

思路:

  在归并的过程中,存在这样的情况,在数组 \(a\) 中,存在一个元素 \(a_i\) 使得 \(a_i > b_{l...r}\),且 \(b_{l...r}\) 对于原数组来说是有序的,这就是一个合法的情况。
  因此,我们的任务就是在原数组里寻找这样的 \(a_i\),以这种数为基准,将原数组划分为几段。如果存在有几段的长度加起来等于 \(n\),便输出 \(YES\),否则输出 \(NO\),这里用动态规划来处理。如序列 \({3,2,6,1,5,7,8, 4}\) 按照这样的规则拆分后得到 \({ \{3,2\} , \{6,1,5,7\} ,\{8,4\} }\),可以发现,第一段和第三段之和为 \(4\),因此它是合法的。

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4005;;
int a[MAXN],n,dp[MAXN];
int k[MAXN];//记录每一段长度 
int t;
int main(){
	cin >> t;
	while(t--){
		scanf("%d",&n);
		for(int i = 1;i <= n + n; i++){
			scanf("%d",&a[i]);
		}
		int st = 1,len = 1;//st为当前这一段的起点,len为现在一共处理的段数 
		for(int i = 2; i <= n + n; i++){
			if(a[i] > a[st]){
				k[len++] = i - st;
				st = i;//更新新一段的起点 
			}
		}
		k[len] = (2 * n - st + 1);//加上最后一段 
		memset(dp,0,sizeof dp);
		dp[0] = 1;
		for(int i = 1; i <= len; i++){
			for(int j = n; j > 0; j--){
				if(j >= k[i]){
					dp[j] |= dp[j - k[i]];
				}
			}
		}
		if(dp[n]){//判断能否构成n 
			printf("YES\n");
		}	
		else printf("NO\n"); 
	}
	return 0;
}
posted @ 2022-07-17 19:35  腾云今天首飞了吗  阅读(31)  评论(0编辑  收藏  举报