前言
突然发现还是一道题一篇博客方便一些qwq... ...
题目解析
描述
由于小凯上次在找零问题上的疑惑,给大家在考场上带来了很大的麻烦,他决心好好学习数学
本次他挑选了位运算专题进行研究 他发明了一种叫做“小凯运算”的运算符:
a$b =( (a&b) + (a|b) )>>1
他为了练习,写了n个数在黑板上(记为a[i]) 并对任意相邻两个数进行“小凯运算”,把两数擦去,把结果留下 这样操作n-1次之后就只剩了1个数,求这个数可能是什么?
将答案从小到大顺序输出
Sample Input
4
1 4 3 2Sample 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;
}