比赛:2021-08-09:A

题目大意

定义一个运算操作,求出一个长度为\(n\)的数列在经过\(n-1\)次合并后能得到几种不同的结果。

题目分析

首先先来观察一下这个\(X\)运算:\(a(X)b=((a\&b)+(a|b))>>1\)
实际上,这个 \(((a\& b)+(a|b))>>1\) 的值就是等于\((a+b)/2\)的值,因为如果\(a\),\(b\)的某一位不同即分别为\(0\)和\(1\),那么\(a\&b\)中这一位为0,\(a|b\)中这一位为1,相加后大小不变。
考虑区间\(DP\)
设一个布尔三维数组\(f\),区间\(i\)到\(j\)能合成\(k\)时\(f_{i,j,k}\)为真,否则为假。
确定\(DP\)的起点:
当所有数都为一个单独的区间时,那么这个区间只能合成这单独的一个数,可以在输入时就初始化:

scanf("%d",&n);
for(int i=1;i<=n;++i){
	scanf("%d",a+i);f[i][i][a[i]]=1;
}

然后开始\(DP\):
在\(DP\)一个区间时,枚举一个分界线,然后再枚举这个分界线两边的两个区间所能合成的值。把两边区间的合成出来的两个值再进行合并,就是当前区间能够合成出来的值。
由于\(a_i \leq 7\),所以能合成出的最大值也为\(7\)。

#include<iostream>
#include<cstdio>
#define sco 200
using namespace std;
int a[sco],n;
bool f[sco][sco][10];
int X(int a,int b){
	return (a+b)/2;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",a+i);f[i][i][a[i]]=1;
	}
	for(int i=2;i<=n;++i){//枚举区间右端点
		for(int j=i;j>0;--j){//枚举区间左端点
			for(int mid=j;mid<=i;++mid){//枚举区间分界线
				for(int l=0;l<=7;++l){//最大值为7
					if(!f[j][mid][l])continue;//如果不能合成当前值就进行下一次循环
					for(int r=0;r<=7;++r){
						if(!f[mid+1][i][r])continue;//同上
						int t=X(l,r);//合并两边区间的合成值
						f[j][i][t]=true;//设置为真
					}
				}
			}
		}
	}
	for(int i=0;i<=7;++i){
		if(f[1][n][i])printf("%d ",i);//输出1~n的区间
	}//输出
}
上一篇:奖学金(归并排序)


下一篇:YBTOJ:划分数列