ZZULIOJ 1921: B 【dp + 位运算】

1921: B

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 270  Solved: 31

Description

给定一个长度为n的数字序列a,从中选取一个长为m的子序列b满足 b[i]&b[i-1]!=0 (2<=i<=m)
求最大的m。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行输入一个整数n代表a序列的长度,接下来一行输入n个正整数表示ai(0<i<=n)。
1<=t<=20,0<=n<=100000,0<=ai<=1e9。

Output

一个整数表示最大的m。

Sample Input

1
3
1 1 1

Sample Output

3

HINT

 

Source

这道题让我一脸懵逼状(⊙﹏⊙)b。又读错题了。。。。我以为是所有子列按位与运算。。。。我说怎么各种WA。。我说为嘛还要dp。。。。。

思路:

用dp记录当前状态下的每一位的最大值,对于序列中的元素, 如果这个数的第j为1, 他肯定要和前面的接上,这样就保证了他和相邻的按位与不为零,然后dp的值等于他后面的加上一。每次检查他的每一位的二进制状态,更新最值。

对于一维的转移方程为 for  dp【i】 = max(dp【last】 + 1, dp【i】); 

#include <bits/stdc++.h>
using namespace std;
const int BIT = 32;
const int maxm = 100000 + 10;
int a, dp[maxm], last[40];
void init() {
    memset(dp, 0, sizeof(dp));
    memset(last, 0, sizeof(last));
}
bool judge_bit(int x, int y) {
    if (x & (1<<y)) return true;
    return false;
}
int main() {
    int n, t;
    scanf("%d", &t);
    while(t--) {
        int ans = 0; scanf("%d", &n); init();
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a);
            for(int j = 0; j <= BIT; j++) {
                if(judge_bit(a, j)) {
                    dp[i] = max(dp[last[j]] + 1, dp[i]);
                    //last记录的是上一次 j 位为 1的位置
                    last[j] = i;
                }
            }
            ans = max(ans, dp[i]);
        }
        printf("%d
", ans);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/cniwoq/p/6770811.html