【区间DP】JZOJ 5935 小凯学数学

前言

突然发现还是一道题一篇博客方便一些qwq... ...

题目解析

描述

        由于小凯上次在找零问题上的疑惑,给大家在考场上带来了很大的麻烦,他决心好好学习数学
        本次他挑选了位运算专题进行研究 他发明了一种叫做“小凯运算”的运算符:
        a$b =( (a&b) + (a|b) )>>1
        他为了练习,写了n个数在黑板上(记为a[i]) 并对任意相邻两个数进行“小凯运算”,把两数擦去,把结果留下 这样操作n-1次之后就只剩了1个数,求这个数可能是什么?
        将答案从小到大顺序输出

Sample Input

4
1 4 3 2

Sample Output

1 2

数据范围

30% n<=10 0<=a[i]<=7
70% n<=150 0<=a[i]<=3
100% n<=150 0<=a[i]<=7

分析

重要的是这个性质:
证明:

以上参考博客:https://blog.csdn.net/er111er/article/details/83586673


看出是区间DP...然后考虑定义式和转移

设f[l][r][k]表示l~r能否合成k(这里的DP是bool的意义,我还是第一次见到)

转移:

研究了一下,终于看懂了:

&:两个都是1才是1,这里代表dp[l][i][a]一定能合成a 并且 dp[i+1][r] 一定能合成b

| :有一个为1就是1,这里代表a+b>>1原先已能被合成或这次被合成

转移过程枚举区间断点、a和b就行,具体看代码注释

参考博客:https://blog.csdn.net/weixin_34378969/article/details/94566299

代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=150;
int a[MAXN+5],dp[MAXN+5][MAXN+5][10];
int n;
int main()
{
	//freopen("math.in","r",stdin);
	//freopen("math.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		dp[i][i][a[i]]=1;
	}
	for(int len=2;len<=n;len++)//枚举区间长度 
		for(int l=1;l<=n-len+1;l++)//区间左端点 
		{
			int r=l+len-1;//区间右端点 
			for(int i=l;i<=r;i++)//枚举区间中的断点 
				for(int a=0;a<=7;a++)//枚举数a 
					for(int b=0;b<=7;b++)//枚举数b 
						dp[l][r][a+b>>1]|=dp[l][i][a]&dp[i+1][r][b];
		}
	for(int i=0;i<=7;i++)
		if(dp[1][n][i])
			printf("%d ",i);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值