额。。。你没有猜错,前面又烂尾了。。
今天讲讲算法。
暴力:
看完题目,直接联想到最长上升子序列。
(最长上升子序列这里就不再讲了,不懂的请参考这里)
时间复杂度O()
代码:
#include <bits/stdc++.h>
using namespace std;
int a[100005], f[100005];
int n,ans = 0;
int main(){
scanf("%d", &n);
for(int i=1; i<=n; i++)
scanf("%d", &a[i]),f[i] = 1;
for(int i=1; i<=n; i++)
for(int j=1; j<i; j++)
if(a[j] & a[i])
f[i] = max(f[i], f[j]+1);
for(int i=1; i<=n; i++)
ans = max(ans, f[i]);
printf("%d\n", ans);
return 0;
}
正解:
拿到题目,我们先看数据范围:n到10的5次方。
考虑O(n log n)。
再看题干,&按位与,显然, &
!=0 的充要条件是
与
转换为二进制后至少有一个对应数位都是1。
设 dp[i][j] 为前 i 个数的每个子序列的最后一个数第 j 位为 1 的最大长度。
则递推式为如果,
在第 j 位都为1:
dp[i][j]=max ( dp[i][j] , dp[i-1][j] + 1 )。
对于每个输入,都跑一遍即可。
时间复杂度:O(n log max () )。
滚动数组优化:
仔细观察递推式,可以发现,每次递推都只与前一个数有关,显然可以使用滚动数组优化。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
int dp[32],a[100005];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int maxn=0;
for(int i=1;i<=n;i++){
maxn=0;
for(int j=0;j<=30;j++)
if(a[i]&(1<<j))
maxn=max(maxn,dp[j]+1);
for(int j=0;j<=30;j++)
if(a[i]&(1<<j))
dp[j]=maxn;
}
for(int i=0;i<=30;i++) maxn=max(maxn,dp[i]);
printf("%d\n",maxn);
return 0;
}