当前位置: 首页 > news >正文

【动态规划】线性dp——LIS和LCS

参考文章

子序列

一个序列 A = a 1 , a 2 , … , a n A=a_1,a_2,…,a_n Aa1,a2,,an 中任意删除若干项,剩余的序列叫做 A 的一个子序列。也可以认为是从序列 A 按原顺序保留任意若干项得到的序列。(例如:对序列{1,3,5,4,2,6,8,7}来说,序列{3,4,8,7}是它的一个子序列。)

LIS 最长上升子序列

代码

转移方程: d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i]=max(dp[i],dp[j]+1) dp[i]=max(dp[i],dp[j]+1)

O(n2)

const int N=5010;
int n;
int a[N],dp[N],ans;
signed main(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=0;i<n;i++){
        dp[i]=1;//自己是一个数列
        for(int j=0;j<i;j++){
            if(a[i]>a[j]){//上升
                dp[i]=max(dp[i],dp[j]+1);//找之前的数列
                ans=max(dp[i],ans);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

O(nlogn)

//O(nlogn)做法 
//下降序列
int ans=1,dp[N]={0};
dp[1]=a[1];
for(int k=2;k<=i;k++){
    if(a[k]<=dp[ans]){
        //更小的向后取组成序列
        ans++;
        dp[ans]=a[k];//下降的序列
    }else{//跟最后的比不下降 就放前面相当于重开一个序列
        //dp下降 用greater<>
        int j=upper_bound(dp+1,dp+1+ans,a[k],greater<int>())-dp;
        //二分 找第一个小于a[k]的数
        dp[j]=a[k];//开下降序列
    }
}
//上升序列
int ans=1,dp[N]={0};
dp[1]=a[1];
for(int k=2;k<=i;k++){
    if(a[k]>dp[ans]){
        ans++;
        dp[ans]=a[k];
    }else{
        //dp上升
        int j=upper_bound(dp+1,dp+1+ans,a[k])-dp;
        //二分 找第一个大于a[k]的数
        dp[j]=a[k];
    }
}

例题

P1020 导弹拦截

构造下降序列找导弹数目,上升序列找系统数目。
二分查找 O(nlogn)做法
细节见代码。

signed main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    int i=1;
    while(cin>>a[i])i++;
    i--;
    //能拦截多少导弹
    int ans=1;
    dp[1]=a[1];
    for(int k=2;k<=i;k++){
        if(a[k]<=dp[ans]){//每一发炮弹都不能高于前一发的高度
            //比前一发低,记录数目
            ans++;
            dp[ans]=a[k];//构造一个下降的序列
        }else{
            //dp下降
            int j=upper_bound(dp+1,dp+1+ans,a[k],greater<int>())-dp;
            //二分 找第一个小于a[k]的数 相当于新开一个系统 新开一个下降序列
            dp[j]=a[k];//开下降序列
        }
    }
    cout<<ans<<endl;
    
    //用多少系统
    int cnt=1;
    x[1]=a[1];
    for(int k=2;k<=i;k++){
        if(x[cnt]<a[k]){//新开一个系统
            cnt++;
            x[cnt]=a[k];//x是上升序列
        }
        else{
            int j=lower_bound(x+1,x+cnt+1,a[k])-x;//找第一个大于等于a[k]的系统 能拦截这个炮弹
            x[j]=a[k];
        }
        // for(int m=1;m<=cnt;m++)cout<<x[m]<<' ';
        // cout<<endl;
    }
    cout<<cnt;
    return 0;
}

P2782 排序+LIS

const int N=2e5+10;
struct fr{
    int a,b;
}c[N];
int dp[N];
void solve(){
    int n;cin>>n;
    forr(i,1,n){
        cin>>c[i].a>>c[i].b;
    }
    sort(c+1,c+1+n,[](fr x,fr y){
        return x.a<y.a;
    });

    //找LIS
    int ans=1;
    /*
    //超时
    forr(i,1,n){
        dp[i]=1;
        forr(j,1,i-1){
            if(c[i].b>c[j].b){
                dp[i]=max(dp[i],dp[j]+1);
                ans=max(dp[i],ans);
            }
        }
    }
    */
   dp[1]=c[1].b;
   forr(i,2,n){
        if(c[i].b>dp[ans]){
            ans++;
            dp[ans]=c[i].b;
        }else{
            int j=upper_bound(dp+1,dp+ans+1,c[i].b)-dp;//替换第一个大于c[i].b的
            dp[j]=c[i].b;
        }
   }
    cout<<ans<<endl;
}

P1091 两次LIS

const int N=2e5+10;
int dp[N],dpr[N];
void solve(){
    int n;cin>>n;
    vector<int>t(n+1);
    forr(i,1,n){
        cin>>t[i];
    }
    int maxn=0;
    forr(i,1,n){
        // dp[i]=1;
        forr(j,1,i-1){
            if(t[i]>t[j])dp[i]=max(dp[j]+1,dp[i]);
        }
    }
    reforr(i,1,n){
        // dpr[i]=1;
        reforr(j,i+1,n){
            if(t[i]>t[j])dpr[i]=max(dpr[j]+1,dpr[i]);
        }
    }
    forr(i,1,n){
        // cout<<dp[i]<<' '<<dpr[i]<<' '<<dp[i]+dpr[i]<<endl;
        maxn=max(dp[i]+dpr[i]+1,maxn);
    }
    cout<<n-maxn<<endl;
}

LCS 最长公共子序列

代码

在这里插入图片描述
状态转移方程 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] + 1 ) dp[i][j]=max(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+1) dp[i][j]=max(dp[i1][j],dp[i][j1],dp[i1][j1]+1),dp是LCS的长度。

forr(i,1,len){
    forr(j,1,len){
        if(s1[i-1]==s2[j-1])dp[i][j]=dp[i-1][j-1]+1;
        else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    }
}

例题

P1435

回文从前面看后面看都是一样的,所以思路就是将原字符串和逆转后的字符串找LCS,就是已经回文的。总长减去回文的就是不回文的。

const int N=1e3+10;
int dp[N][N];
void solve(){
    string s;cin>>s;
    string rs=string(s.rbegin(),s.rend());
    int len=s.size();
    forr(i,1,len){
        forr(j,1,len){
            if(s[i-1]==rs[j-1])dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
    cout<<len-dp[len][len];
}
http://www.dtcms.com/a/109462.html

相关文章:

  • 网站提示“不安全“怎么办?原因分析与解决方法
  • sqlalchemy详细介绍以及使用方法
  • 防爆对讲机VS非防爆对讲机,如何选择?
  • 将现有 REST API 转换为 MCP Server工具 -higress
  • element-plus中,表单校验的使用
  • HCIP-8 ospf静默接口,ospf树
  • 用Python和Pygame创造粉色粒子爱心:3D渲染的艺术
  • 《AI大模型应知应会100篇》第56篇:LangChain快速入门与应用示例
  • VTK 各个点颜色的设置
  • 289.将所有数字用字符替换
  • 栈容器的应用
  • 代码随想录|动态规划|17一和零
  • DAY 35 leetcode 202--哈希表.快乐数
  • QT 非空指针 软件奔溃
  • 检索增强生成(RAG) 优化策略
  • LLM架构解析:长短期记忆网络(LSTM)(第三部分)—— 从基础原理到实践应用的深度探索
  • MyBatis:开源、轻量级的数据持久化框架
  • 游戏引擎学习第201天
  • Muduo网络库实现 [九] - EventLoopThread模块
  • 康谋分享 | 仿真驱动、数据自造:巧用合成数据重构智能座舱
  • 去中心化交易所(DEX)
  • Unity3D开发AI桌面精灵/宠物系列 【三】 语音识别 ASR 技术、语音转文本多平台 - 支持科大讯飞、百度等 C# 开发
  • 【GCC警告报错4】warning: format not a string literal and no format arguments
  • 从扩展黎曼泽塔函数构造物质和时空的结构-13
  • 练习题:120
  • 【Linux学习笔记】开发工具git和gbd和cgbd的介绍和使用
  • PADS 9.5【附破解文件+安装教程】中文激活版下载
  • IP分片攻击实战模拟
  • 蓝桥杯真题——传送阵
  • xsync脚本是一个基于rsync的工具