P4310 绝世好题 [位运算优化dp]

Posted vocanda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4310 绝世好题 [位运算优化dp]相关的知识,希望对你有一定的参考价值。

题目描述

给定一个长度为 (n) 的数列 (a_i)?,求 (a_i)? 的子序列 (b_i)? 的最长长度 (k),满足 (b_i & b_{i-1} e 0),其中 (2leq ileq k)(&) 表示位运算取与。

输入格式

输入文件共 (2) 行。 第一行包括一个整数 (n)。 第二行包括 (n) 个整数,第 (i) 个整数表示 (a_i)?。

输出格式

输出文件共一行。 包括一个整数,表示子序列 (b_i)? 的最长长度。

输入输出样例

输入 #1

3
1 2 3

输出 #1

2

说明/提示

对于(100\%)的数据,(1leq nleq 100000)(a_ileq 10^9)

分析

看到题的第一反应就被标签里的枚举给迷惑到了,然后快速打了一个暴力枚举,显然直接(90)(TLE)了,然后开始想正解。

首先题目中的条件是含有位运算的,我们就可以从这里开始入手优化。

因为题目中给的条件是(a_i&a_{i-1} eq 0),所以这就证明了只要两个数之间有一个二进制位上相同的(1)就可以让长度加一,那么我们就可以枚举每一个二进制位,找出他们与运算后是否为(0),如果不是(0)就让长度加一,我们的一个状态转移方程就出来了:

[Max = max(dp[c]+1,Max) ]

其中(dp[c])是二进制第(c)位时最大的长度,当前状态下满足条件就加一,然后与最大长度(Max)取最大值。

需要注意的一个地方就是我们在状态转移后需要每次更新一下(dp[c]),因为当前满足条件的最长长度已经找出来是(Max)了,所以所有当前满足条件的二进制位的最大长度都变为(1),那么就又有一个转移:

[dp[c]=Max ]

最后再统计一下所有更新后(Max)的最大值就好了。

代码

首先先放一下我的暴力(90points)代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int n,a[maxn],ans;
int dp[maxn];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;++i){
		dp[i]=1;
		for(int j=1;j<i;++j){
			if((a[i]&a[j])!=0)dp[i]=max(dp[i],dp[j]+1);
		}
		ans=max(ans,dp[i]);
	}
	printf("%d",ans);
	return 0;
}

然后是正解:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 32;
int dp[maxn];
int Max,ans;
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int b;
		scanf("%d",&b);
		for(int c=0;c<=31;++c){//枚举每一个二进制位
			if((1<<c)&b)Max=max(Max,dp[c]+1);
		}
		for(int c=0;c<=31;++c){//更新满足条件的二进制位的最大值
			if((1<<c)&b)dp[c]=max(Max,dp[c]);
		}
		ans=max(ans,Max);//统计最大值答案
	}
	cout<<ans<<"
";
	return 0;
}

以上是关于P4310 绝世好题 [位运算优化dp]的主要内容,如果未能解决你的问题,请参考以下文章

P4310 绝世好题 题解

绝世好题

BZOJ 4300 绝世好题(位运算)

bzoj 4300 绝世好题——DP

绝世好题-按位DP

bzoj4300: 绝世好题(DP)