272. 最长公共上升子序列(lcis,dp)

2023-11-13

首先是lis的状态划分图

然后是lcs

结合lis和lcs两种dp问题的分析方法,我们就可以得出lcis的状态分析图

1.首先上升子序列的分析方法:以某个数字为结尾

2.其次公共子序列的分析方法:有4种状态 00,01,10,11

!!!双关键字的问题一般都以“消元”掉某个关键字来进行降维处理

状态表示

集合:所有以a[1,i]和b[1,j]构成的且以b[j]为结尾的公共上升子序列

属性:max

状态计算(决策集合)

不包含a[i]:那么这个集合的最大值=dp[i-1][j]

包含a[i]:在包含a[i]时,我们发现它有多种状态子集,需要进一步划分

由于需要上升子序列,所以要对集合中的各个元素进行遍历

因为所有以b[k]结尾的公共上升子序列的最大值一定是dp[i][k](简单可证)

所以我们枚举 Σ(1~j)max(dp[i][j],dp[i-1][k]+1)

        子序列只包含b[j]一个数,长度是1;
        子序列的倒数第二个数是b[1]的集合,最大长度是f[i - 1][1] + 1;
        …
        子序列的倒数第二个数是b[j - 1]的集合,最大长度是f[i - 1][j - 1] + 1;

dp[i-1][k]是i-1的原因为:定义出发,dp[i][k]是考虑了a[1,i]和b[1,k],dp[i-1][k]是考虑了a[1,i-1]和b[1,k]

如果采用dp[i][k]会导致a[i]被重复计算了两次,可能b[k]==b[j]

决策集合:dp[i][j]的决策集合,所有以a[1..i-1]和b[1,k]元素构成,且以b[k]结尾的公共上升子序列集合

O(n^3)

#include <iostream>
#include <algorithm>
using namespace std;
const int N=3000+5;
int a[N];
int b[N];
int dp[N][N];
int t[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
        cin>>b[i];
    int res=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            dp[i][j]=dp[i-1][j];    //比较包含a[i]和不包含哪个更好

            if(a[i]==b[j])
            {
                for(int k=0;k<j;k++)
                    if(b[k]<b[j])
                        dp[i][j]=max(dp[i-1][k]+1,dp[i][j]);
            }
        }
    }
    for(int i=1;i<=n;i++)
        res=max(res,dp[n][i]);
    cout<<res;
}

O(n^2)优化

如何优化?

1.寻找重复操作

2.寻找性质优化

重复操作:在第三重循环时,我们在寻找以b[j]结尾的lcis时,会有重复操作

寻找以b[j]为结尾时,我们需要遍历一遍前面1~j-1  所有元素

寻找以b[j+1]为结尾时,我们需要遍历一遍前面1~j  所有元素

我们可以注意到 b1~bj-1这一段元素是可以不用再次遍历的,存在重复操作。

对于决策集合来说,遍历到j+1,j进入,集合中的元素只增加不减少

那么引用前缀和的思想,引入一个前缀最大值来维护前面的最长公共上升子序列长度

寻找性质优化:

我们注意到一个值被放入决策集合中时,都是在b[j]==a[j]这个大前提条件下的

if(a[i]==b[j])
{
     for(int k=0;k<j;k++)
        if(b[k]<b[j])    //这里也可以写成b[k]<a[i] 因为a[i]==b[j]
          dp[i][j]=max(dp[i-1][k]+1,dp[i][j]);
}
 

 在第二重循环1~j时,i是固定的,即a[i]也是固定的

由于一个子序列能够放入集合中条件a[i]>b[k],那么我们可以在第二重循环中维护一个前缀最大值

for(int i=1;i<=n;i++)
    {//maxv表示前缀最大值(前面最长的公共上升子序列+1)
        int maxv=1; //最差情况是本身
        for(int j=1;j<=n;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(a[i]==b[j])  //a[i]是公共子序列,更新值
                dp[i][j]=max(dp[i][j],maxv);
            //b[j]被放入j+1的决策集合中,检查
            if(a[i]>b[j])   //a[i]>b[k] 才表示可以放入
                maxv=max(maxv,dp[i-1][j]+1);
        }
    }

 问题:

1.为什么可以用a[i]>b[j]代替

我们要求的是最长公共上升子序列

假设a[i]==b[j],第二重循环时,a[i]会逐渐和1~j-1中所有元素进行比较,如果a[i]>b[k](k表示此时的j),那么等价于b[j]>b[k],如果不存在这个b[j],循环结束后,dp[i][...]都是0,如果存在这个b[j],那么维护的这个前缀最大值可以派上用场

2.为什么maxv和dp[i-1][j]取max,而不是f[i][j]

和dp[i-1][j]取max是继承上一步On^3的思路,f[i][j]表示a[i]已经和b[j]匹配过了,我们maxv维护的是所有包含a[i]且以b[j]为结尾的子序列最大长度,应该是a[i]还没有匹配过。

应是dp[i-1][j]+1        //1表示ai被匹配, 

换成dp[i][j]和maxv能过是因为

如果b[j]==b[j+1],他们不是上升的子序列,不会被重复计数,取max时可以重复,但是思路是错误的,如果取不严格上升子序列,用f[i][j]会出错

#include <iostream>
#include <algorithm>
using namespace std;
const int N=3000+5;
int a[N];
int b[N];
int dp[N][N];
int t[N];
int g[N][N];    //一个决策集合
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
        cin>>b[i];
    
    int res=0;
    for(int i=1;i<=n;i++)
    {//maxv表示前缀最大值(前面最长的公共上升子序列+1)
        int maxv=1; //最差情况是本身
        for(int j=1;j<=n;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(a[i]==b[j])  //a[i]是公共子序列,更新值
                dp[i][j]=max(dp[i][j],maxv);
            //b[j]被放入j+1的决策集合中,检查
            if(a[i]>b[j])   //a[i]>b[k] 才表示可以放入
                maxv=max(maxv,dp[i-1][j]+1);
        }
    }
    for(int i=1;i<=n;i++)
        res=max(res,dp[n][i]);
    cout<<res;
}

总结

实现转移方程时,要注意观察决策集合的范围随着状态的变化情况,对于决策集合元素只  增加不减少  的情况,可以像本题一样维护一个变量来记录决策集合的当前信息,避免重复扫描,达到降维的作用

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

272. 最长公共上升子序列(lcis,dp) 的相关文章

  • 算法入门Bu1:排序

    算法入门 BuBuBu 相关数据结构 栈 队列 链表 树 并差集 堆 图等 相关算法 排序 枚举 深度和广度优先搜索 图的遍历 图中最短路径算法 最小生成树算法 割点和割遍算法 二分图的最大匹配算法等 排序算法 简单的桶排序 特点 如果需要
  • Java HashMap底层实现

    HashMap 是 Java 使用频率最高的用于映射 键值对 处理的数据类型 JDK1 8 对 HashMap 底层的实现进行了优化 例如引入红黑树的数据结构和扩容的优化等 在JDK1 8以前HashMap是由数组 链表的数据结构组成的 J
  • 【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

    整理 串口 RS232 RS485等 通讯中RTS CTS DTR DSR的含义详解 RS232 crifan 7年前 2013 10 17 14942浏览 0评论 背景 之前就折腾过很多关于RTS CTS DTR DSR的内容 整理 RT
  • excel 文档管理服务器,Excel Server Tutorial

    在企事业单位的实际业务中所需要使用的信息 除了数据之外 还包括文档 文档是各种类别和格式的 它们可能是Word文件 如企业的规章制度 可能是AutoCAD文件 如产品设计图纸 也可能是视频 音频文件 如内部培训资料 等等 通常的管理信息系统

随机推荐

  • 计算之魂思考题1.4赛跑问题

    一 问题 假设由25名短跑者争夺比赛前三名 赛场上有5条赛道 一次可以有5名选手同时比赛 比赛不计时 只看相应名次 假设选手发挥稳定 也就是说如果约翰比张三跑得快 张三比凯莉跑得快 那么约翰一定比凯莉跑得快 最少需要几次比赛才能决出前三名
  • 墨者学院的靶场之SQL注入漏洞测试(布尔盲注)

    这是我作为小白第一次成功的把key找出来了 分享一下 墨者学院的靶场SQL注入漏洞测试 布尔盲注 IP地址 219 153 49 228 端口 40205 协议 http 首先我输入了IP地址和端口号 看到这个后 我按了F12看了后台的代码
  • 【华为OD机试】食堂供餐【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 某公司员工食堂以盒饭方式供餐 为将员工取餐排队时间降低为0 食堂的供餐速度必须要足够快 现在需要根据以往员工取餐的统计信息 计算出一个刚好能达成排队时间为0的最低供餐
  • 【华为OD机试真题】【python】 网上商城优惠活动(一)【2022 Q4

    华为OD机试 题目列表 2023Q1 点这里 2023华为OD机试 刷题指南 点这里 题目描述 某网上商场举办优惠活动 发布了满减 打折 无门槛3种 优惠券 分别为 1 每满100元优惠10元 无使用数限制 如100 199元可以使用1张减
  • Ubuntu18.04/16.04调整屏幕分辨率至1920*1080

    Ubuntu在设置 显示里面的分辨率选择没有1920 1080 我们可以手动增加该分辨率配置 然后进行设置 参考文字和图片 crtl alt t 打开终端窗口 获取添加分辨率的格式 输入 cvt 1920 1080 查看显示器信息及已经支持
  • 安卓HAL层 so库文件加载原理

    本文分析代码基于安卓6 0 上层app通过jni调用hal层的hw get module函数获取硬件模块 这个函数是上层与hal打交道的入口 这里我们就具体来看看hw get module的实现 文件路径 vim hardware libh
  • andorid 与滑动相关的一些知识---getscrollY onscrollchange() scrollby scrollto等

    Android系统手机屏幕的左上角为坐标系 同时y轴方向与笛卡尔坐标系的y轴方向想反 通过提供的api如getLeft getTop getBottom getRight可以获得控件在parent中的相对位置 同时 也可以获得控件在屏幕中的
  • python怎么升级python的pip

    报错提示 WARNING You are using pip version 19 1 1 however version 20 0 2 is available You should consider upgrading via the
  • ouldn‘t check the working tree for unmerged files because of an error. detected dubious ownership in

    IDEA的git的clone操作中如果出现问题 couldn t check the working tree for unmerged files because of an error detected dubious ownershi
  • LangChain 的聊天模型

    各位人工智能爱好者 大家好 今天 我们就来详细了解一下 LangChain 聊天模型 LangChain是一个很棒的工具 它提供了与各种语言模型交互的标准接口 包括基于文本的大型语言模型 LLM 和聊天模型 LangChain模型的概念 模
  • 【Linux命令详解

    文章标题 简介 一 参数列表 二 使用介绍 1 使用基本模式搜索 2 忽略大小写匹配 3 反向匹配 4 递归搜索目录 5 显示文件名 6 显示行号 7 显示上下文行 8 启用扩展正则表达式 9 将模式视为固定字符串 10 使用颜色高亮显示匹
  • Docker部署服务

    部署Nginx 寻找镜像 docker search nginx 默认最新版 官网查看不同的版本信息 下载镜像 docker pull nginx root iZwz9hv1phm24s3jicy8x1Z docker images REP
  • C语言打印输出斐波那契数前20项案例讲解

    我们先看什么是斐波那契数 斐波那契数列指的是这样一个数列 1 1 2 3 5 8 13 21 34 55 89 我们通过观察可以得出斐波那契数列的特点 1 第一项和第二项都是1 2 这个数列从第3项开始 每一项都等于前两项之和 思路分析 1
  • 2023年第二届全国大学生数据统计与分析竞赛题目B:电影评分的大数据分析第二问

    详细代码 企鹅2869955900 import pandas as pd import matplotlib pyplot as plt import numpy as np plt rcParams font sans serif Si
  • 【大数据】Hadoop 生态系统及其组件

    Hadoop 生态系统及其组件 1 Hadoop 生态系统的组成 2 Hadoop 生态系统简介 2 1 HDFS 2 2 MapReduce 2 3 YARN 2 4 Hive 2 5 Pig 2 6 HBase 2 7 HCatalog
  • python3 模块、import、from import

    模块 1 模块就是 py后缀的文件 2 py文件类似于一个类 包含以下部分 1 导入 一般的类都有导入 2 变量 对应类的属性 3 函数 对应类的方法 4 类 对应内部类 5 if name main 对应主函数 6 顶格写的代码段 对应构
  • OpenCV学习笔记(17)双目测距与三维重建的OpenCV实现问题集锦(二)双目定标与双目校正

    三 双目定标和双目校正 双目摄像头定标不仅要得出每个摄像头的内部参数 还需要通过标定来测量两个摄像头之间的相对位置 即右摄像头相对于左摄像头的三维平移 t 和旋转 R 参数 图6 要计算目标点在左右两个视图上形成的视差 首先要把该点在左右视
  • vue2+高德地图web端开发使用

    创建vue2项目 我们创建一个vue2项目 创建vue2项目就不用再多说了吧 使用 vue create 项目名 创建即可 注册高德地图 高德地图官网地址 https lbs amap com 如果是第一次使用 点击注册然后进入我们的控制台
  • idea快捷键最全最新最好

    持续更新 如果文档中没有的 麻烦在评论中添加 常用快捷键 返回最顶头 home 返回最末尾 end Alt Insert 可以新建类 文件 get或set方法 此快捷键又名创造一切 编辑区和文件区的跳转 alt 1 编辑区跳转至文件区 es
  • 272. 最长公共上升子序列(lcis,dp)

    首先是lis的状态划分图 然后是lcs 结合lis和lcs两种dp问题的分析方法 我们就可以得出lcis的状态分析图 1 首先上升子序列的分析方法 以某个数字为结尾 2 其次公共子序列的分析方法 有4种状态 00 01 10 11 双关键字