【算法提高:动态规划】1.2 最长上升子序列模型(TODO:最长公共上升子序列)

news2024/11/27 8:41:10

文章目录

  • 题目列表
    • 1017. 怪盗基德的滑翔翼
    • 1014. 登山
    • 482. 合唱队形
    • 1012. 友好城市(⭐排序后 最长上升子序列模型)
    • 1016. 最大上升子序列和
    • 1010. 拦截导弹
      • 解法1——最长递减子序列 + 贪心
      • 解法2——最长递减子序列 + 最长递增子序列(⭐贪心结论)
    • 187. 导弹防御系统⭐⭐⭐⭐⭐(至少需要多少个 上升/下降 子序列)(dfs + 最少需要多少最长上升子序列)⭐⭐⭐⭐⭐
    • 272. 最长公共上升子序列⭐⭐⭐⭐⭐
      • 解法1——一起计算🚹TODO
      • 解法2——先求最长公共子序列,再求最长上升子序列长度🚹TODO
  • 补充相关题目列表
    • 673. 最长递增子序列的个数
    • 115. 不同的子序列
  • 相关链接

题目列表

1017. 怪盗基德的滑翔翼

https://www.acwing.com/problem/content/1019/
在这里插入图片描述
注意 基德 可以从任意地方开始选择向左或向右出发。
所以需要 dp 两次。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        int k = sin.nextInt();
        while (k-- != 0) {
            int n = sin.nextInt(), ans = 1;
            int[] a = new int[n], dp = new int[n];
            Arrays.fill(dp, 1);
            for (int i = 0; i < n; ++i) {
                a[i] = sin.nextInt();
                for (int j = 0; j < i; ++j) {
                    if (a[i] < a[j]) dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                ans = Math.max(ans, dp[i]);
            }
            Arrays.fill(dp, 1);
            for (int i = n - 1; i >= 0; --i) {
                for (int j = n - 1; j > i; --j) {
                    if (a[i] < a[j]) dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                ans = Math.max(ans, dp[i]);
            }
            System.out.println(ans);
        }
    }
}

1014. 登山

https://www.acwing.com/problem/content/1016/
在这里插入图片描述
因为一旦开始向下走之后就不会再往上走了。

所以向上走的状态只能从向上走的状态转移过来;向下走的状态既能从向上走的状态转移过来,也可以从向下走的状态转移过来。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        int n = sin.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; ++i) a[i] = sin.nextInt();
        int[][] dp = new int[n][2];
        int ans = 1;
        for (int i = 0; i< n; ++i) {
            dp[i][0] = dp[i][1] = 1;
            for (int j = 0; j < i; ++j) {
                if (a[i] > a[j]) {
                    dp[i][0] = Math.max(dp[i][0], dp[j][0] + 1);
                }
                else if (a[i] < a[j]) {
                    dp[i][1] = Math.max(dp[i][1], Math.max(dp[j][0], dp[j][1]) + 1);
                }
            }
            ans = Math.max(ans, Math.max(dp[i][0], dp[i][1]));
        }
        System.out.println(ans);
    }
}

482. 合唱队形

https://www.acwing.com/problem/content/484/
在这里插入图片描述
计算每个节点作为结尾位置时向左和向右的递减子序列的最长长度即可。

注意最后要求的答案是需要几位同学出列 而不是 最多保留几位同学。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        int n = sin.nextInt();
        int[] t = new int[n];
        for (int i = 0; i < n; ++i) t[i] = sin.nextInt();
        int[][] dp = new int[n][2];
        for (int i = 0; i < n; ++i) {
            dp[i][0] = dp[i][1] = 1;
            for (int j = 0; j < i; ++j) {
                if (t[i] > t[j]) dp[i][0] = Math.max(dp[i][0], dp[j][0] + 1);
            }
        }
        int ans = 1;
        for (int i = n - 1; i >= 0; --i) {
            for (int j = n - 1; j > i; --j) {
                if (t[i] > t[j]) dp[i][1] = Math.max(dp[i][1], dp[j][1] + 1);
            }
            ans = Math.max(ans, dp[i][0] + dp[i][1] - 1);
        }
        System.out.println(n - ans);
    }
}

1012. 友好城市(⭐排序后 最长上升子序列模型)

https://www.acwing.com/problem/content/1014/

在这里插入图片描述

按照一岸排序,求另一岸此时的最长递增子序列。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        int n = sin.nextInt();
        int[][] a = new int[n][2];
        for (int i = 0; i < n; ++i) {
            a[i][0] = sin.nextInt();
            a[i][1] = sin.nextInt();
        }
        // 按照一岸排序
        Arrays.sort(a, (x, y) -> x[0] - y[0]);
        // 求另一岸此时的最长递增子序列
        int ans = 0;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (a[i][1] > a[j][1]) dp[i] = Math.max(dp[i], dp[j] + 1);
            }
            ans = Math.max(ans, dp[i]);
        }
        System.out.println(ans);
    }
}

1016. 最大上升子序列和

https://www.acwing.com/problem/content/1018/

在这里插入图片描述

跟最长上升子序列长度的方法差不多,只修改了递推公式部分,将 + 1 修改成了 + a[i]。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        int n = sin.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; ++i) a[i] = sin.nextInt();
        int[] dp = new int[n];
        Arrays.setAll(dp, i -> a[i]);
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (a[i] > a[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + a[i]);
                }
            }
            ans = Math.max(ans, dp[i]);
        }
        System.out.println(ans);
    }
}

1010. 拦截导弹

https://www.acwing.com/problem/content/1012/

在这里插入图片描述
使用最长上升子序列模型解决问题1。(对于这题是个最长递减子序列)。

对于问题2,当一个新的导弹来临时,我们首先检查是否有已经存在的系统可以拦截它。如果可以,我们应该选择一个能够拦截这个新来的导弹并且当前最高高度最低的系统来拦截它
结论:从上面的贪心可以推出来,是在求最长上升子序列。

解法1——最长递减子序列 + 贪心

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        final int N = 1001;
        int[] a = new int[N], dp = new int[N];
        int n = 0;
        while (sin.hasNext()) {
            a[n++] = sin.nextInt();
        }

        // 使用动态规划求解最多能拦截的导弹数
        int ans1 = 0;
        Arrays.fill(dp, 1);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (a[i] <= a[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            ans1 = Math.max(ans1, dp[i]);
        }
        System.out.println(ans1);
		
		// 贪心
        List<Integer> ls = new ArrayList<>();
        for (int i = 0; i < n; ++i) {
            int k = 0;
            while (k < ls.size() && ls.get(k) < a[i]) k++;
            if (k >= ls.size()) ls.add(a[i]);
            else ls.set(k, a[i]);
        }
        System.out.println(ls.size());
    }
}

解法2——最长递减子序列 + 最长递增子序列(⭐贪心结论)

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        final int N = 1001;
        int[] a = new int[N], dp = new int[N], dp2 = new int[N];
        int n = 0;
        while (sin.hasNext()) {
            a[n++] = sin.nextInt();
        }
		
		// 不递增和递增子序列的最大长度
        int ans1 = 0, ans2 = 0;
        Arrays.fill(dp, 1);
        Arrays.fill(dp2, 1);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (a[i] <= a[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                } else dp2[i] = Math.max(dp2[i], dp2[j] + 1);
            }
            ans1 = Math.max(ans1, dp[i]);
            ans2 = Math.max(ans2, dp2[i]);
        }
        System.out.println(ans1 + "\n" + ans2);
    }
}

187. 导弹防御系统⭐⭐⭐⭐⭐(至少需要多少个 上升/下降 子序列)(dfs + 最少需要多少最长上升子序列)⭐⭐⭐⭐⭐

https://www.acwing.com/problem/content/189/

在这里插入图片描述
注意数据范围是 50 比较小。

在上一题的代码框架基础上 增加一个 dfs 爆搜。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    final static int N = 52;
    static int[] q = new int[N], up = new int[N], down = new int[N];
    static int ans = 0, n = 0;

    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        while (true) {
            n = sin.nextInt();
            if (n == 0) return;
            ans = 0;
            dfs(0, 0, 0);
            System.out.println(ans);
        }
    }

    // u,su,sd 表示枚举到了第一个数,当前上升子序列的个数,当前下降子序列的个数
    static void dfs(int u, int su, int sd) {
        if (su + sd >= ans) return;
        if (u == n) {
            ans = su + sd;
            return;
        }

        // 情况1:将当前数放到上升子序列中
        int k = 0;
        while (k < su && up[k] >= q[u]) k++;
        int t = up[k];
        up[k] = q[u];
        if (k < su) dfs(u + 1, su, sd);
        else dfs(u + 1, su + 1, sd);
        up[k] = t;      // 恢复现场

        // 情况2:将当签署放到下降子序列中
        k = 0;
        while (k < sd && down[k] <= q[u]) k++;
        t = down[k];
        down[k] = q[u];
        if (k < sd) dfs(u + 1, su, sd);
        else dfs(u + 1, su, sd + 1);
        down[k] = t;
    }
}

Q:为什么不用 bfs 来求解?
A:因为 bfs 需要的空间太大,可能会爆炸。(bfs 存一层,dfs 存一个路径)。

272. 最长公共上升子序列⭐⭐⭐⭐⭐

https://www.acwing.com/problem/content/274/

在这里插入图片描述

解法1——一起计算🚹TODO

dp[i][j] 表示 两个数组 0 ~ i 和 0 ~ j 之间的 且以 b[j] 为结尾的 最长公共上升子序列长度。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        int n = sin.nextInt();
        int[] a = new int[n], b = new int[n];
        for (int i = 0; i < n; ++i) a[i] = sin.nextInt();
        for (int i = 0; i < n; ++i) b[i] = sin.nextInt();

        // dp[i][j] 表示 两个数组 0 ~ i 和 0 ~ j 之间的最长公共上升子序列长度。
        int[][] dp = new int[n + 1][n + 1];
        for (int i = 1; i <= n; ++i) {
            int maxV = 1;      // 可以上升的长度
            for (int j = 1; j <= n; ++j) {
                dp[i][j] = dp[i - 1][j];
                if (a[i - 1] == b[j - 1]) dp[i][j] = Math.max(dp[i][j], maxV);  	// 相等,是公共子序列
                if (b[j - 1] < a[i - 1]) maxV = Math.max(maxV, dp[i - 1][j] + 1);	// 更新可以上升的长度
            }
        }
        int ans = Arrays.stream(dp[n]).max().getAsInt();
        System.out.println(ans);
    }
}

解法2——先求最长公共子序列,再求最长上升子序列长度🚹TODO

在这里插入代码片

补充相关题目列表

673. 最长递增子序列的个数

https://leetcode.cn/problems/number-of-longest-increasing-subsequence/description/
在这里插入图片描述

在这里插入代码片

115. 不同的子序列

https://leetcode.cn/problems/distinct-subsequences/description/

在这里插入图片描述
提示:

1 <= s.length, t.length <= 1000
s 和 t 由英文字母组成

class Solution {
    public int numDistinct(String s, String t) {
        int m = s.length(), n = t.length();
        // dp[i][j] 表示考虑s从0~i-1的子序列中,以t[0:j]出现的次数(注意这里j就表示j-1结尾了,i表示考虑0~i-1)
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 0; i <= m; ++i) dp[i][0] = 1;      // s[0:i-1]中出现""的次数是1
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                dp[i][j] = dp[i - 1][j];                // 至少也和 i - 1 一样
                // 如果一样,说明当前i-1可以匹配到j-1结尾,从i-1,j-1转移过来
                // 转移是 + ,因为求的是出现的个数:i-1匹配到了说明出现了新一批匹配完全的s中以i-1为结尾和t[0:j]匹配上的子序列
                if (s.charAt(i - 1) == t.charAt(j - 1)) dp[i][j] += dp[i - 1][j - 1];
            }
        }
        return dp[m][n];
    }
}

相关链接

【算法】最长递增子序列:动态规划&贪心+二分查找

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/793867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年你不容错过的软件测试项目实战(APP项目实战)免费版

前言 最近很多的人都在问我有没有什么项目可以用来练手&#xff0c;正好我这里有一个比较适合练手的项目&#xff0c;那就给大家安利一下吧&#xff0c;废话就不多说了。 项目名称&#xff1a; APP项目实战 项目说明&#xff1a; 本项目里面包括了功能测试、性能测试、安全…

MATLAB与ROS联合仿真——实例程序搭建思路

一、基础运动控制实例程序搭建思路 1、需要完成的任务&#xff1a; &#xff08;1&#xff09;通过设定小车运动的速度及转角来控制ROS中小车运动。 &#xff08;2&#xff09;通过键盘输入指令控制ROS中小车运动&#xff0c;键盘输入w小车前行&#xff0c;s小车后退&#x…

[golang gin框架] 42.Gin商城项目-微服务实战之后台Rbac微服务角色增删改查微服务

一.重构后台Rbac用户登录微服务功能 上一节讲解了后台Rbac微服务用户登录功能以及Gorm数据库配置单独抽离&#xff0c;Consul配置单独抽离&#xff0c;这一节讲解后台Rbac微服务角色增删改查微服务功能&#xff0c;Rbac微服务角色增删改查微服务和后台Rbac用户登录微服务是属于…

Vue--Vuex

一、Vuex 概述 1.是什么 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。例如&#xff1a;购物车数据 个人信息数 2.使用场景 某个状态 在 很多个组件 来使…

leetCode刷题记录3-面试经典150题

文章目录 不要摆&#xff0c;没事干就刷题&#xff0c;只有好处&#xff0c;没有坏处&#xff0c;实在不行&#xff0c;看看竞赛题面试经典 150 题80. 删除有序数组中的重复项 II189. 轮转数组122. 买卖股票的最佳时机 II 不要摆&#xff0c;没事干就刷题&#xff0c;只有好处&…

169. 多数元素

题目 题解一&#xff1a;map集合计数 /*** map集合计数* param nums* return*/public static int majorityElement(int[] nums) {Map<Integer, Integer> map new HashMap<>();//第一个for循环将数组中的元素作为key 出现次数作为value存入map 并且key重复 就v…

代码随想录算法学习心得 48 | 583.两个字符串的删除操作、72.编辑距离...

一、两个字符串的删除操作 链接&#xff1a;力扣 描述&#xff1a;给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 思路如下&#xff1a;整体思路是不变的。 这次是两个字符串可以相互…

OceanMind海睿思助力南京钢铁基于数字化审计为核心的全域风控管理

近日&#xff0c;中新赛克海睿思 与 南京钢铁股份有限公司&#xff08;以下简称“南钢”&#xff09;达成深度战略合作&#xff0c;携手推进企业内审数字化迈向智慧化发展。 双方将依托 OceanMind海睿思 提供业内领先的大数据平台和丰富的审计行业经验&#xff0c;积极利用先进…

《向量数据库指南》:向量数据库Pinecone备份索引教程

目录 ⚠️警告 使用集合创建备份 检查集合的状态 列出您的集合 删除一个集合 本文档描述如何使用集合备份索引。 要了解如何从集合创建索引,请参阅管理索引。 ⚠️警告 本文档使用集合。这是一个公开预览功能。在使用此功能生产负载之前,请进行充分测试。 使用集合…

一种有趣的 OTA 升级思路(基于 LoRa 通信的 OTA 固件升级的调试记录)

文章目录 1 概述2 调试之路2.1 想法2.2 函数和变量定义在绝对地址的实现2.2.1 IAR的扩展关键字2.2.2 函数的绝对定位2.2.3 变量的绝对定位2.2.4 常量的绝对定位2.2.4 .c文件的绝对定位 2.3 Bootload 共有函数的实现2.4 APP 共有函数的使用 3 注意事项4 调试坎坷之路5 补充 1 概…

学习React(四)

学习React&#xff08;四&#xff09; componentWillMount&#xff08;被放弃使用&#xff09;rendercomponentDidMountshouldComponentUpdatecomponentWillUpdate&#xff08;被放弃使用&#xff09;componentDidUpdatecomponentWillReceiveProps&#xff08;被放弃使用&#…

idea连接远程服务器上传war包文件

idea连接远程服务器&上传war包 文章目录 idea连接远程服务器&上传war包1. 连接服务器2.上传war包 1. 连接服务器 选择Tools -> Start SSH Session 添加配置 连接成功 2.上传war包 Tools -> Deployment -> Browse Remote Host 点击右侧标签&#xff0c;点击&…

【二开】JeecgBoot-vue3二次开发 前端 扩展online表单js增强等-在表单里拿到列表上下文onlineTableContext

【二开】JeecgBoot-vue3二次开发 前端 扩展online表单js增强等-在表单里拿到列表上下文 onlineTableContext 对应的属性方法 acceptHrefParams"<p> 跳转时获取的参数信息"currentPage"<p> 当前页数"currentTableName"<p> 当前表…

读取有重复section的ini格式文件最新的几个不同section

文件内容示例如下 可以看到文件并不是标准的 ini 配置文件的格式&#xff0c;存在很多重复的 section&#xff08;中括号里的就是section&#xff09; &#xff0c; 我的任务是读取文件末尾最新的四个不同 section&#xff0c;并发送出去。 按照读取 ini 文件那样读取显然是不…

常用API学习08(Java)

格式化 格式化指的是将数据按照指定的规则转化为指定的形式 。 那么为什么需要格式化&#xff1f;格式化有什么用&#xff1f; 以数字类为例&#xff0c;假设有一个比分牌&#xff0c;在无人得分的时候我们希望以&#xff1a;“00&#xff1a;00”的形式存在&#xff0c;那么…

睡眠健康数据分析

项目背景 背景描述 本数据集涵盖了与睡眠和日常习惯有关的诸多变量。如性别、年龄、职业、睡眠时间、睡眠质量、身体活动水平、压力水平、BMI类别、血压、心率、每日步数、以及是否有睡眠障碍等细节。 数据集的主要特征&#xff1a; 综合睡眠指标&#xff1a; 探索睡眠持续时…

Gitlab服务器备份恢复及系统升级

居安思危&#xff0c;思则有备&#xff0c;有备无患。 基于此&#xff0c;申请了一个测试服务器&#xff0c;准备先安装同版本服务器&#xff0c;按照最新的数据进行恢复&#xff0c;然后再将现在的服务器升级到Gitlab的最新版本&#xff0c;记录一下完整的过程&#xff0c;以…

文件上传漏洞总结2

文件上传的大体都已经学习过了 这个假期在给他强化一下 什么是webshell webshell是web入侵的脚本攻击工具。webshell就是一个asp或php木马后门&#xff0c;黑客在入侵了一个网站后&#xff0c;常常在将这些asp或php木马后门文件放置在网站服务器的web目录中&#xff0c;与正常…

C++初阶之一篇文章让你掌握string类(了解和使用)

string类及其模拟实现 1.我们为什么要学习string类2. 标准库中的string类2.1 string类的实例化标准2.2 了解string 3.string类的常用接口说明3.1 string类对象的常见构造3.2 string类对象的容量操作3.3 string类对象的元素访问3.4 string类对象的Iterators&#xff08;迭代器&a…

Websocket协议-http协议-tcp协议区别和相同点

通讯形式 单工通讯-数据只能单向传送一方来发送数据&#xff0c;另一方来接收数据 半双工通讯-数据能双向传送但不能同时双向传送 全双工通讯-数据能够同时双向传送和接受 注&#xff1a;http的通讯方式是分版本 http1.0&#xff1a;单工。因为是短连接&#xff0c;客户端…