模拟赛20210809A T1

模拟赛20210809A T1

题目

在这里插入图片描述

大意:一个长度为 n n n的序列,求经过 n − 1 n-1 n1次合并操作后所有可能的结果(从小到大输出)
定义合并操作为:每次操作可将任意一对相邻的数 a , b a,b a,b 合并成 ( ( a & b ) + ( a ∣ b ) > > 1 ((a\&b)+(a | b)>>1 ((a&b)+(ab)>>1

思路

区间DP:
f i , j , k f_{i,j,k} fi,j,k 表示 i i i j j j 这个区间内能否经过若干次合并得到 k k k,能为1,不能为0。
因为 ( ( a & b ) + ( a ∣ b ) > > 1 ((a\&b)+(a | b)>>1 ((a&b)+(ab)>>1 实际上等于 ( a + b ) / 2 (a+b)/2 (a+b)/2,而 a i a_i ai最大为7,所以所有结果都不会超过7。

第一层循环枚举区间长度
第二层枚举区间位置(一个端点)
第三层枚举中间点 m i d mid mid,表示将区间分为左区间 [ i , m i d ] [i,mid] [i,mid] 和右区间 [ m i d + 1 , j ] [mid+1,j] [mid+1,j]
第四层枚举左区间能合并成的结果 p p p,即 f [ i ] [ m i d ] [ p ] = = 1 f[i][mid][p]==1 f[i][mid][p]==1
第五层枚举右区间能合并成的结果 q q q,即 f [ m i d + 1 ] [ j ] [ q ] = = 1 f[mid+1][j][q]==1 f[mid+1][j][q]==1

因为左区间能合并成 p p p,右区间能合并成 q q q,所以整个区间能合并成 ( p + q ) / 2 (p+q)/2 (p+q)/2
即当 f [ i ] [ m i d ] [ p ] = = 1 f[i][mid][p]==1 f[i][mid][p]==1 f [ m i d + 1 ] [ j ] [ q ] = = 1 f[mid+1][j][q]==1 f[mid+1][j][q]==1 时 ,    f [ i ] [ j ] [ ( p + q ) / 2 ] = 1 \ \ f[i][j][(p+q)/2]=1   f[i][j][(p+q)/2]=1
最后判断 f 1 , n , i f_{1,n,i} f1,n,i中可以合并的数。

因为 1 < = i < = m i d < j < = n 1<=i<=mid<j<=n 1<=i<=mid<j<=n     0 < = k < = 7 \ \ \ 0<=k<=7    0<=k<=7
所以时间复杂度为跑不满的 O ( n 3 8 2 ) = O ( 64 n 3 ) , 对 于 此 题 n < = 150 是 可 以 过 的 O(n^38^2)=O(64n^3),对于此题n<=150是可以过的 O(n382)=O(64n3)n<=150

代码

#include<iostream>
#include<cstdio>
#define reg register
using namespace std;
int n,a[160];
bool f[160][160][10];
int mer(int a,int b)//合并操作
{
	return (((a&b)+(a|b))>>1);
}
int main()
{
	scanf("%d",&n);
	for(reg int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		f[i][i][a[i]]=true;//初始化
	}
	for(reg int i=2;i<=n;++i)//枚举区间长度
	{
		for(reg int j=i;j>=1;--j)//枚举区间左端点(要倒着)
		{
			for(reg int mid=j;mid<i;++mid)//枚举中间点
			{
				for(reg int l=0;l<=7;++l)//枚举左区间能不能合并的数
				{
					if(!f[j][mid][l]) continue;//不能就跳过
					for(reg int r=0;r<=7;++r)//枚举右区间能不能合并的数
					{
						if(!f[mid+1][i][r]) continue;//不能就跳过
						int t=mer(l,r);//合并操作
						f[j][i][t]=true;
					}
				}
			}
		}
	}
	for(reg int i=0;i<=7;++i)
	{
		if(f[1][n][i]) printf("%d ",i);//判断并输出
	}
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值