Luogu P4310 绝世好题 (dp)
题目大意:
给出一个数列a,求a的子序列b的最长长度,满足\(b_i \& b_{i-1} \neq 0\)。
思路:
很容易联想到\(O(n^2)\)求最长上升子序列。
for i in range(1, n + 1):
dp[i] = 1
for j in range(1, i):
if (a[i] & a[j]) != 0:
dp[i] = max(dp[i], dp[j] + 1)
ans = max(ans, dp[i])
接下来考虑怎么优化。
结合按位与的运算法则可以发现只要两个数之间存在一位二进制位上有相同的1就可以让长度加一,即可的到状态转移方程。
考虑一个样例
3
3 6 4
将三个数化为对应的二进制形式即
011
110
100
可以发现“110”中的第一个1的dp值同样为2,这就提醒我们在每次状态转移之后更新dp[i]。
Code:
import java.io.*;
import java.util.*;
public class Main {
public static final double eps = 1e-6;
public static final int N = 32;
public static final int INF = 0x3f3f3f3f;
public static final int mod = 1000000007; //998244353
public static int[] dp; //dp[i]为当前二进制位为第i位时满足要求的最长长度
public static int maxx; //当前满足条件的最大长度
public static int ans;
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
int n = (int)st.nval;
dp = new int[N];
for (int t = 1; t <= n; t++) {
st.nextToken();
int a = (int)st.nval;
for (int i = 0; i <= 31; i++) {
if (((1 << i) & a) != 0) { //由于是求子序列,只要当前数a的第i位二进制数位1,就能使dp[i]+1
maxx = Math.max(maxx, dp[i] + 1); //只要相邻两个数之间有一个二进制位上相同的1就可以让长度加一
}
}
for (int i = 0; i <= 31; i++) {
if (((1 << i) & a) != 0) {
dp[i] = Math.max(dp[i], maxx); //当前满足条件的二进制为的最大长度都为maxx
}
}
ans = Math.max(maxx, ans);
}
System.out.println(ans);
}
}
/*
3
3 6 4
5
5 2 5 2 5
5
1 5 5 1 1
*/