绝世好题!!!——有操作的dp思维题

引子:

那天…我们和初二的同学们一起做了题。
T1 我认为是有必要写一些题解来加深一点印象。
因为自己在做这道题的时候,也是有点懵。


思路(1):

dp(最长上升子序列):

用这个方法呢,是没有问题的,时间复杂度是(N*logn)。因为题目的要求是:

求ai的子序列bi的最长长度。

所以就是可以用 最 长 上 升 子 序 列 。 \color{#FF3030}{最长上升子序列。}
即状态转移方程

f[i]=max{f[j]+1}且j<i且b[i]&b[j]!=0

但是!注意!

这道题因为你想要AC的话必须要用位运算!

为什么?!那就看下面

思路(2):

二进制优化 (我也不知道到底应该叫什么。):

因为答案只能是由两个在二进制表示下至少有一位同是1的a序列里的数&得到的,最后求子序列的个数f[i]存的是对于a序列中当前遍历到的数中有几个在二进制表示下第i位为1,因为求的是子序列的个数而非方案,所以发现当前的数可以与之前的数&之后得到1满足题目要求,答案就只+1令dp[i]表示数列到目前为止最后一项第i位为1的最大子序列长度,每读入一个数时就大力转移。一个数可以被它所有的二进制位的dp值转移,然后把它转移到它的所有二进制位的dp值上。

有的同学要问了,怎样求某个数二进制的第i位咧?这时候位运算就闪亮登场了。x&(1<

我们来举个栗子:

1 2 3.

转为二进制为:01 10 11

0.f[0]=0  f[1]=0;
1.f[0]=1  f[1]=0;
2.f[0]=1  f[1]=1;
3.f[0]=2  f[1]=2;
ans=2;

1 3 2 4

转为二进制为:001 011 010 100

0:f[0]=0 f[1]=0 f[2]=0
1:f[0]=1 f[1]=0 f[2]=0
2:f[0]=2 f[1]=max(f[1]+1=1,f[0]=2)=2,f[2]=0
3:f[0]=2 f[1]=3 f[2]=0
4:f[0]=2 f[1]=3 f[2]=1
ans=3

20分代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 103, INF = 0x7f7f7f7f;//乱开的数组额...反正只有二十
int a[maxn], f[maxn];
int n,ans = -INF;
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;
}

AC代码:

#include
#include 
using namespace std;
int max(int x, int y) { return x > y ? x : y; }
int n, dp[35];
int main() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++){
    	int x;
        scanf("%d", &x);
        int ansx = 1;
        for (int i = 0; i <= 30; ++i)
            if (x & (1 << i)) {//如果这一位是1
            
                ansx = max(ansx, dp[i] + 1);//长度++,并取最大进行f值的转移
            }
        for (int i = 0; i <= 30; ++i)
            if (x & (1 << i)) {
                dp[i] = max(dp[i], ansx);
            }
    }
    int ans = 0;
    for (int i = 0; i <= 30; ++i) {
        ans = max(ans, dp[i]);
    }
    printf("%d", ans);
    return 0;
}

总结:这道题思路非常明显。但是我们要知道,为什么只有位运算才行,这是本题最想考你的东西。

不然就太水了

你可能感兴趣的:(DP)