绝世好题 (位运算优化dp)
题目描述
给定一个长度为 n n n 的数列 a i a_i ai,求 a i a_i ai 的子序列 b i b_i bi 的最长长度 k k k,满足 b i & b i − 1 ≠ 0 b_i \& b_{i-1} \ne 0 bi&bi−1=0,其中 2 ≤ i ≤ k 2\leq i\leq k 2≤i≤k, & \& & 表示位运算取与。
输入格式
输入文件共 2 行。
第一行包括一个整数
n
n
n。
第二行包括
n
n
n 个整数,第
i
i
i 个整数表示
a
i
a_i
ai。
输出格式
输出文件共一行。
包括一个整数,表示子序列
b
i
b_i
bi 的最长长度。
样例 #1
样例输入 #1
3
1 2 3
样例输出 #1
2
提示
对于100%的数据, 1 ≤ n ≤ 100000 1\leq n\leq 100000 1≤n≤100000, a i ≤ 1 0 9 a_i\leq 10^9 ai≤109。
非常难顶的一道题,我几乎把所有题解都看完了,只有几篇能看懂,有些题解表达能力不敢恭维
思维过程:
虽然明知 O ( N 2 ) O(N^2) O(N2) 复杂度会 T T T , 还是要写一发。
ll n,m,_;
int a[N],f[N];
void solve(){
cin>>n;
fo(i,1,n)cin>>a[i];
fo(i,1,n){
f[i] = 1;
for(int j=1;j<i;j++){
if((a[j] & a[i]) !=0){
f[i] = max(f[i],f[j]+1);
}
}
}
int ans = 0;
fo(i,1,n)ans = max(ans,f[i]);
cout<<ans<<endl;
}
int main(){
solve();
return 0;
}
从二进制位的方向考虑,一个贪心的想法就是某位二进制都为 1 1 1 的集合个数的最大值,但是明显有反例
设想这样7个数:
00001
00001
00001
00011 (一个桥梁)
00010
00010
00010
答案不是4而是7
假定正在考虑第 I I I 个数,设 $f[i] $ , ( i 和 I 不 同 ) (i和I不同) (i和I不同) 表示第 i i i 位二进制数都为 1 1 1 的最大序列长度。
考虑转移到 f [ i ] f[i] f[i] (以 a [ i ] a[i] a[i] 结尾的所有满足题目条件选法的集合)的情况,只有和 a [ i ] a[i] a[i] 在某一二进制位上都为 1 1 1 的一组数所组成的序列 b b b 才能转移到 f [ i ] f[i] f[i] 。
比如:
输入:
3
1 3 5 (假设最低位标号为1)
----------
1:001 f[1]=1
3:011 f[1]=2,f[2]=1 ; 因为3由于第1位可以跟在1后边,所以f[2]=2
5:101 f[1]=2,f[2]=2 ; f[1]=3,由于f[1]=3导致f[3]=3;
----------
设想这样7个数:
00001
00001
00001
00011 (一个桥梁)
00010
00010
00010
-----------
最后答案明显是7
感觉有点DP套DP,就是和一般的线性dp不同。
int f[30],ans=0;
void solve(){
cin>>n;
fo(i,1,n){
int x;cin>>x;
int tmp = 0;
for(int j=0;j<30;j++){
if(x&(1<<j)){
tmp = max(f[j]+1,tmp);
}
}
for(int j=0;j<30;j++){
if(x&(1<<j)){
f[j] = tmp;
}
}
}
for(int i=0;i<30;i++)
ans=max(ans,f[i]);
cout<<ans<<endl;
}