最长(图论/动规)

第2题     最长 查看测评数据信息

给定一个长度是n的序列a[1,2,...n],现在你需要构造一个长度为m的数组b[1,2,...m],需要满足如下条件:

(1)b是a的子序列

(2)b[i]&b[i-1]!=0 (2<=i<=m)

问构造出b后,m的最大值是多少。

输入格式

 

输入文件共2行。第一行包括一个整数n。

第二行包括n个整数,第i个整数表示a[i]。

1<=n<=1e5,1<=a[i]<=1e9

 

输出格式

 

一个整数,表示m的最大值

 

输入/输出例子1

输入:

3

1 2 3

 

输出:

2

 

样例解释

 

位运算都考虑拆位!一位一位看!

做法一:
f[i]:以第i个数结尾满足的最长序列
记录lst数组,第i进制位是1属于的最近的a数组下标

https://www.luogu.com.cn/article/ncpi87q5

 

 

做法二:
逐步考虑每一位
对于每一位,二进制位数上是1的可以建一个图,例如

a的第3个进制位是1
b的第3个进制位是1
那么我们建 a->b 的有向边

图保证无环,我们找图的最长链就是答案(拓扑)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
 
int n, b[N], d[N], ans=0, dis[N];
vector<int> a[N];
queue<int> q;
void topsort()
{
    q.push(0);
     
    while (!q.empty())
    {
        int u=q.front();
        q.pop();
         
        for (int i=0; i<a[u].size(); i++)
        {
            int v=a[u][i];
             
            if (dis[v]<dis[u]+1) dis[v]=dis[u]+1;
            ans=max(ans, dis[v]);
             
            d[v]--;
            if (!d[v]) q.push(v);
        }
    }
}
int main()
{
    scanf("%d", &n);
    for (int i=1; i<=n; i++) scanf("%d", &b[i]);
     
    for (int k=0; k<=32; k++)
    {
        int last=0;
        for (int i=1; i<=n; i++)
            if (b[i]&(1<<k))
            {
                a[last].push_back(i);
                d[i]++;
                last=i;
            }
    }
         
    topsort();
     
    printf("%d", ans);
    return 0;
}

  


做法三:

覆盖,因为两数想要按位与不为0,那么证明它们有一二进制位都是1,我们就把他们的二进制位上是1的位置,在 f 数组里+1,但是要注意,是根据 f 数组中的最大值+1,这样不断去覆盖它,不就相当于试探每个数字按位与一下吗

最终 f 数组中最大的值即为答案

https://www.luogu.com.cn/article/w99xswjw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
const int N=50;
 
int n, a, f[N], ans=0;
int main()
{
    scanf("%d", &n);
    for (int i=1; i<=n; i++)
    {
        int t=0, Max=0, cnt=0;
        scanf("%d", &a);
         
        t=a;
        while (t>0)
        {
            cnt++;
            if (t&1) Max=max(Max, f[cnt]);
            t>>=1;
        }
         
        cnt=0, t=a;
        while (t>0)
        {
            cnt++;
            if (t&1) f[cnt]=max(Max+1, f[cnt]);
            t>>=1;
        }
    }
     
    for (int i=1; i<=32; i++) ans=max(ans, f[i]);
     
    printf("%d", ans);
    return 0;
}

  

 

 

 

 

 

posted @   cn是大帅哥886  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示