题目大意
定义一个运算操作,求出一个长度为\(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的区间
}//输出
}