大数据LIS (贪心+二分优化/树状数组优化)

该文主要介绍了如何解决寻找两个全排列的最长公共子序列问题,通过将排列重排并转化为求最长上升子序列,使用了动态规划和树状数组两种优化方法来实现。动态规划方案是将一个排列改造成升序,然后构建最长上升子序列;树状数组方案则用于快速更新和查询序列长度。
摘要由CSDN通过智能技术生成

P1439 【模板】最长公共子序列 - 洛谷 

题目描述(原线性dp)

给出 1,2,…,n 的两个排列 P1​ 和 P2​ ,求它们的最长公共子序列。

输入格式

第一行是一个数 n。

接下来两行,每行为 n 个数,为自然数 1,2,…,n 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

输入输出样例

输入 #1

5 
3 2 1 4 5
1 2 3 4 5

输出 #1

3

说明/提示

  • 对于 50%50% 的数据, n≤103;
  • 对于 100%100% 的数据, n≤105。

思路:

两个序列求最长公共子序列,根据题意,两个序列都是1-n的全排列,我们可以利用map进行重排,把第一个序列改成上升序列,那么原问题就转化为了求序列的最长上升子序列 

贪心+二分优化:

我们枚举map重排后的新数列,维护一个答案数组是上升子序列,如果当前元素大于答案数组最后一个元素,直接加进去答案数组

否则,在答案数组里找见第一个大于当前元素的元素,将其修改为当前元素

因为如果要这个元素构造出的新数列比没有这个元素的答案数列长,那么这个元素之前的元素和答案数列前面的元素相同,后面的与答案数列无关,直接替换掉第一个大于它的元素即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
int main(){
    
    cin>>n;
    vector<int>a(n+1);
    
    map<int,int>m;
    for(int i=1;i<=n;i++){
        int t;
        cin>>t;
        m[t]=i;     //map重排
    }
    
    for(int i=1;i<=n;i++){
        int t;
        cin>>t;
        a[i]=m[t];
    }
    
    vector<int>ans;
    ans.push_back(a[1]);
    
    for(int i=2;i<=n;i++){
        if(a[i]>ans[ans.size()-1])ans.push_back(a[i]);  //添加至最长递增子序列末端
        else{
            int t=upper_bound(ans.begin(),ans.end(),a[i])-ans.begin();  //找第一个大于的数
            ans[t]=a[i];    //更改为a[i]
        }
    }
    cout<<ans.size();
    return 0;
}

 

 树状数组优化

之前思路一样,然后利用树状数组储存当前元素为 i 时的最大的序列长度

每次枚举新数列,修改大于这个数的 f [ i ] 为当前数列的长度+1

用当前数列长度更新答案即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;

int f[100005];      //树状数组 f[i] 记录当前元素为i时LIS序列的元素个数

int query(int x){   //查询小于等于x的序列个数
    int ans=0;
    while(x){
        ans=max(ans,f[x]);
        x-=(x&(-x));
    }
    return ans;
}

void modify(int x,int y){
    while(x<100005){
        f[x]=max(f[x],y);
        x+=x&(-x);
    }
}

int main(){
    cin>>n;
    vector<int>a(n+1);
    map<int,int>m;      //映射处理,使数组1变成上升序列
    
    for(int i=1;i<=n;i++){
        int t;
        cin>>t;
        m[t]=i;
    }
    
    for(int i=1;i<=n;i++){      //求数组2的LIS
        int t;
        cin>>t;
        a[i]=m[t];
    }
    
    int ans=0;
    for(int i=1;i<=n;i++){
    
        modify(a[i],query(a[i])+1);     //大于等于a[i]的数存的序列长度为当前长度+1
    
        ans=max(ans,query(a[i]));       //查询小于等于当前值的序列长度
    }
    
    cout<<ans;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Auroraaaaaaaaaaaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值