绝(zhen)世(xiang)好题题解
我好菜啊
前言
我是真的菜(╥╯^╰╥),挠了半天的头,还是只想到了O(n * n)的算法 不过骗了80分, 头 挠得 疼的实在受不了,搜了一下题解,看了之后看懂了,可觉得讲的不是很详细,秉承着不浪费每一道题的 素质 精神,我决定写篇blog,希望这样也能让我记忆深刻。
ps:黎老师连题目名都不改的吗,我搜索能力这么差都搜到了
先摆出状态转移方程
f[i][j] = max(f[i - 1][k]) (a[i]在二进制表示下,第j, k位为1)
f[i - 1][j] (a[i]在二进制表示下,第j位为0)
分析
我们定义f[i][j]为:在前i个数中选择,以在二进制表示下,第j位为1的数结尾的最长的序列长度。
那么我们分析一下f[i][j]会由哪些状态转移过来。
1.第i个数在二进制表示下的第j位为1
那么第i个数可以作为f[i][j]的末尾(因为第j位在"&"之后肯定是1)
所以f[i][j] = f[i - 1][j] + 1; (???)
NO!
因为只需关注序列中最后两个数(因为前面的都是满足要求的),所以只要在二进制表示下,最后的两数有同一数位(二进制表示下)为1就可以满足要求,那么我们就只需要在满足要求的方法中选最大值
所以 f[i][j] = max(f[i - 1][k]) (a[i]在二进制表示下,第k位为1)
2.不为1
这种比较简单,就是:f[i][j] = f[i - 1][j];
所以状态转移方程为
f[i][j] = max(f[i - 1][k]) (a[i]在二进制表示下,第j, k位为1)
f[i - 1][j] (a[i]在二进制表示下,第j位为0)
实现
最后加个滚动数组,减少判断和代码量。
细节参考代码
#include <cstdio>
const int MAXLEN = 105;
const int MAXN = 100005;
const int INF = 0x3f3f3f3f;
int n, ans = -INF, a[MAXN];
int f[MAXLEN];
//f[i][j]表示在前i个中选,选出来的数的最后一个,在二进制表示下,第j位为1的最长序列长度
int Max(int x, int y) {return x > y ? x : y;}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) {
int _max = -INF;
//存储在前i个数中选择,以第j位为1的数结尾的最长序列
for(int j = 1; j <= 32; j++) {
if(((a[i] >> (j - 1)) & 1) == 1)//第i个数的第j位为1,可以作为f[j]的末尾
_max = Max(_max, f[j] + 1);//"拼"在之前的以第j位为1的数结尾的序列
}
for(int j = 1; j <= 32; j++)
if((a[i] >> (j - 1) & 1) == 1) f[j] = Max(f[j], _max);//第i个数的第j位为1,可以作为f[j]的末尾
ans = Max(ans, _max);
}
printf("%d", ans);
return 0;
}