绝(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;
}

欢迎纠错

posted @ 2020-06-16 22:59  C2022lihan  阅读(40)  评论(0编辑  收藏  举报