算法【Java】—— 动态规划之子序列问题

最长递增子序列

https://leetcode.cn/problems/longest-increasing-subsequence

在这里插入图片描述


状态表示:和之前的经验一样,dp[i] 表示 以 i 为结尾元素的所有递增子序列中最大长度是多少

状态转移方程推导:从 i 前面的元素开始寻找,当 nums[j] < nums[i] 的时候,则找到一个递增子序列,比较出最大长度,dp[i] = max(dp[i], dp[j] + 1)

初始化:由于一个元素就可以构成一个序列,所以将 dp 表所有元素初始化为 1

填表顺序:从方程中得出,填表过程中需要得到前面的状态值,那么我们需要从左到右开始填表。

返回值:返回最大长度即可,在填表的过程中比较寻找即可

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int ret = 1;
        for(int i = 1; i < n; i++) {
            for(int j = i-1; j >= 0; j--) {
                if(nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

摆动序列

https://leetcode.cn/problems/wiggle-subsequence

在这里插入图片描述


状态表示:dp[i] 以 i 为结尾的 摆动子序列的最大长度,向前搜索,如果当 nums[j] > nums[i] 那么此时的差值为负数,我们后面要接一个结尾的差值为正数的子序列,这时候会发现现在这个 dp 表示不能满足。
我们需要细分 dp 值,f[i] 表示以 i 为结尾的 摆动子序列 且 最后一个差值为正数 的最大长度
g[i] 表示以 i 为结尾的 摆动子序列 且 最后一个差值为负数 的最大长度

状态转移方程推导:当 nums[i] > nums[j], f[i] = Math.max(f[i], g[j] + 1)
当 nums[i] < nums[j]) g[i] = Math.max(g[i], f[j] + 1)

初始化:一个元素也可以构成摆动子序列,所以将两个 dp 表 初始化为 1

填表顺序:从左到右

返回值:返回两个 dp 表的最大值

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        Arrays.fill(f, 1);
        Arrays.fill(g, 1);
        int ret = 1;
        for(int i = 1; i < n; i++) {
            for(int j = i - 1; j >= 0; j--) {
                if(nums[i] > nums[j]) {
                    f[i] = Math.max(f[i], g[j] + 1);
                } else if(nums[i] < nums[j]) {
                    g[i] = Math.max(g[i], f[j] + 1);
                }
            }
            ret = Math.max(ret, Math.max(f[i], g[i]));
        }
        return ret;
    }
}

最长递增子序列的子数

https://leetcode.cn/problems/number-of-longest-increasing-subsequence

在这里插入图片描述


状态表示:这里使用两个 dp 表,len[i] 表示 以 i 结尾的元素的子序列的最大长度是多少
count[i] 表示 以 i 结尾的 最大长度的子序列有多少个

状态转移方程推导:len 很简单就是向前遍历 nums[i] > nums[j] 就判断是否要更新最大长度
count 就要小心谨慎,如果 nums[i] 跟新了最大长度,那么 此时 count[i] 应该等于 count[j]
如果遇到 nums[i] > nums[j] 且 最大长度不用更新,也就是说找到了另一个相同长度的子序列,这时候 count[i = count[j]

初始化:因为一个元素也可以构成一个序列,所以 len 表初始化为 1
由于一个元素可以构成一个序列,此时最大长度的序列就只有一个 ,count 表初始化为 1

填表顺序:因为要用到前面的状态值,所以从左到右开始填表

返回值:先记录最大长度,然后遍历 len 表找到最大长度的子序列,从 count 表获取序列个数,然后相加即可

class Solution {
    public int findNumberOfLIS(int[] nums) {
        int n = nums.length;
        int[] len = new int[n];
        int[] count = new int[n];
        Arrays.fill(len, 1);
        Arrays.fill(count, 1);
        int ret = 0;
        int max_len = 1;
        for(int i = 1; i < n; i++) {
            for(int j = i-1; j >= 0; j--) {
                if(nums[i] > nums[j]) {
                    if(len[i] == len[j] + 1) {
                        count[i] += count[j];
                    } else if(len[i] < len[j] + 1) {
                        count[i] = count[j];
                        len[i] = len[j] + 1;
                    }
                }
            }
            max_len = Math.max(max_len, len[i]);
        }
        for(int i = 0; i < n; i++) {
            if(len[i] == max_len) {
                ret += count[i];
            }
        }
        return ret;
    }
}

最长数对链

https://leetcode.cn/problems/maximum-length-of-pair-chain

在这里插入图片描述


解析:这道题目很像上面的最大长度的子序列问题,我们可以转化一下,先对二维数组进行升序排序,接下来就是常规的子序列问题了

class Solution {
    public int findLongestChain(int[][] pairs) {
        Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
        int n = pairs.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int ret = 1;
        for(int i = 1; i < n; i++) {
            for(int j = i - 1; j >= 0; j--) {
                if(pairs[i][0] > pairs[j][1]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

最长定差子序列

https://leetcode.cn/problems/longest-arithmetic-subsequence-of-given-difference

在这里插入图片描述


dp[i] 表示 以 i 元素为结尾的 最大长度的等差序列 的长度。

正常来说,我们需要加一个 向前遍历的 循环来找到 对应的等差数列, 当 nums[j] = nums[i] - difference 的时候,我们需要将 dp[i] 更新为 dp[j] + 1,为什么不需要进行 dp[i] = Math.max 的操作,因为这是定差等差数列,所以 nums[i] 前一个元素是唯一的,即使有很多个 nums[j] 满足 nums[i] 的条件也无所谓,在从后向前的循环中,我们只需要找到一个 nums[j] 就可以退出 j 的 循环。

处于后置位 的 nums[j] 的等差数列是一定包含 前置位的 nums[j] 的序列,并且后置位的 nums[j] 的长度是大于等于前置位的。

这里要注意超时问题,如果数据量过大,两次循环的时间就不行,那么我们需要进行优化,如何 快速得到nums[j] ???
这里可以使用 HashMap 来保存 nums[j] 和 j 下标
那么此时时间复杂度就为 O(N)

class Solution {
    public int longestSubsequence(int[] arr, int difference) {
        int n = arr.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int ret = 1;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(arr[0], 0);
        for(int i = 1; i < n; i++) {
            if(map.containsKey(arr[i] - difference)) {
                dp[i] = dp[map.get(arr[i] - difference)] + 1;
            }
            map.put(arr[i], i);
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

最长的斐波那契子序列的长度

https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence

在这里插入图片描述


构成斐波那契额数列的条件为 n > 3 , x1 + x2 = x3
因此斐波那契额数列至少需要三个元素,因此当返回值小于 3 的时候,这时候就没有斐波那契数列,直接返回 0

状态表示:从斐波那契数列构成的条件来看,需要三个数字,我们可以定义一个二维的 dp 表,dp[i][j] 表示以 i 和 j 结尾的元素的斐波那契数列的最大长度,因为只要确定最后两个元素,就可以确定 前一个 斐波那契数。
我们来固定一下,i 元素 是 起始元素,j 元素是 结尾元素,i 在 j 的前面

状态转移方程的推导:计算出 第三个斐波那契额数的 数值 arr[k] = arr[j] - arr[i],k 要满足 k < j 这个条件
如何找到 arr[k] ,我们有两种思路,第一种就是加一层 for 循环,但是这样时间复杂度就变成了 n ^ 3
第二种思路就是使用 哈希表,保存 arr[k] 和 下标 k
dp[i][j] = max(dp[i][j], dp[k][i] + 1)

初始化:dp[i][j] 表示以 i 和 j 为结尾的斐波那契额数列,长度为 1 或者 2,我们可以直接将 dp 表初始化为 2,虽然当 i == j 的 dp 值是 1,但是这个数值我们是用不到的,因为我们填表的时候满足 i < j

填表顺序:

返回值:返回最大长度的 dp 值,当最大长度 小于 3 直接返回 0

class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        int n = arr.length;
        int[][] dp = new int[n][n];
        for(int i = 0; i < n; i++) {
            Arrays.fill(dp[i], 2);
        }
        int ret = 0;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(arr[0], 0);
        map.put(arr[1], 1);
        for(int j = 2; j < n; j++) {
            for(int i = j - 1; i > 0; i--) {
                int k = map.getOrDefault(arr[j] - arr[i], -1);
                if(k != -1 && k < i) {
                    dp[i][j] = Math.max(dp[i][j], dp[k][i] + 1);
                }
                ret = Math.max(ret, dp[i][j]);
            }
            map.put(arr[j], j);
        }
        return ret < 3 ? 0 : ret;
    }
}

最长等差数列

https://leetcode.cn/problems/longest-arithmetic-subsequence

在这里插入图片描述


和上一道题类似,dp[i][j] 表示以 i 和 j 元素结尾的等差数列的最大长度

使用哈希表进行优化

class Solution {
    public int longestArithSeqLength(int[] nums) {
        int n = nums.length;
        int[][] dp = new int[n][n];
        for(int i = 0; i < n; i++) {
            Arrays.fill(dp[i], 2);
        }
        Map<Integer, Integer> map = new HashMap<>();
        map.put(nums[0], 0);
        int ret = 2;
        for(int i = 1; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                int k = map.getOrDefault(2 * nums[i] - nums[j], -1);
                if(k != -1 && k < i) {
                    dp[i][j] = Math.max(dp[i][j], dp[k][i] + 1);
                }
                ret = Math.max(ret, dp[i][j]);
            }
            map.put(nums[i], i);
        }
        return ret;
    }
}

等差数列划分 Ⅱ - 子序列

https://leetcode.cn/problems/arithmetic-slices-ii-subsequence

在这里插入图片描述


dp[i][j] 表示 以 i 和 j 结尾的元素 【假定 i 在 j 后面】的等差数列一共有多少

状态转移方程推导:tmp = nums[j] - nums[i] ,开始向前遍历寻找 tmp, 把所有包含 tmp 的状态值获取,dp[i][j] += dp[k][i] + 1

dp[k][i] 表示以 k 和 i 元素为结尾的 等差数列,这里由于多出一个 j 元素,因此还需要加上 以 k, i ,j 这三个元素构成的等差数列,所以应要 + 1.

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        int n = nums.length;
        int[][] dp = new int[n][n];
        int ret = 0;
        for(int i = 1; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                long tmp = (long)(2 * (long)nums[i]) - (long)nums[j];
                for(int k = i - 1; k >= 0; k--) {
                    if(nums[k] == tmp) {
                        dp[i][j] += dp[k][i] + 1;
                    }
                }
                ret += dp[i][j];
            }
        }
        return ret; 
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET Core标识框架Identity

目录 Authentication与Authorization 标识框架&#xff08;Identity&#xff09; Identity框架的使用 初始化 自定义属性 案例一&#xff1a;添加用户、角色 案例二&#xff1a;检查登录用户信息 案例三&#xff1a;实现密码的重置 步骤 Authentication与Authorizatio…

124,【8】buuctf web [极客大挑战 2019] Http

进入靶场 查看源码 点击 与url有关&#xff0c;抓包 over

windows下安装Open Web UI

windows下安装openwebui有三种方式,docker,pythonnode.js,整合包. 这里我选择的是第二种,非docker. 非Docker方式安装 1. 安装Python&#xff1a; 下载并安装Python 3.11&#xff0c;建议安装路径中不要包含中文字符&#xff0c;并勾选“Add python 3.11 to Path”选项。 安…

Mac 基于Ollama 本地部署DeepSeek离线模型

最近节日期间最火的除了《哪吒》就是deepseek了&#xff0c;毕竟又让西方各个层面都瑟瑟发抖的产品。DeepSeek凭借其强大的AI能力真的是在全球多个领域展现出强大的影响力。由于受到外部势力的恶意攻击倒是deepseek官方服务不稳定&#xff0c;国内其他厂家的适配版本也不是很稳…

解决aspose将Excel转成PDF中文变成方框的乱码问题

原文网址&#xff1a;解决aspose将Excel转成PDF中文变成方框的乱码问题_IT利刃出鞘的博客-CSDN博客 简介 本文介绍如何解决aspose将Excel转成PDF中文变成方框的乱码问题。 问题描述 用aspose将word、excel等转成PDF后&#xff0c;英文展示正常&#xff0c;但中文全部变成了…

Jupyter Notebook自动保存失败等问题的解决

一、未生成配置文件 需要在命令行中&#xff0c;执行下面的命令自动生成配置文件 jupyter notebook --generate-config 执行后会在 C:\Users\用户名\.jupyter目录中生成文件 jupyter_notebook_config.py 二、在网页端打开Jupyter Notebook后文件保存失败&#xff1b;运行代码…

【漫话机器学习系列】083.安斯库姆四重奏(Anscombe‘s Quartet)

安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09; 1. 什么是安斯库姆四重奏&#xff1f; 安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09;是一组由统计学家弗朗西斯安斯库姆&#xff08;Francis Anscombe&#xff09; 在 1973 年 提出的 四组数据集。它们…

Axure设计教程:动态排名图(中继器实现)

一、开篇 在Axure原型设计中&#xff0c;动态图表是展示数据和交互效果的重要元素。今天&#xff0c;我们将学习如何使用中继器来创建一个动态的排名图&#xff0c;该图表不仅支持自动轮播&#xff0c;还可以手动切换&#xff0c;极大地增强了用户交互体验。此教程旨在提供一个…

MySQL视图索引操作

创建学生表&#xff1b; mysql> create table Student(-> Sno int primary key auto_increment,-> Sname varchar(30) not null unique,-> Ssex char(2) check (Ssex男 or Ssex女) not null,-> Sage int not null,-> Sdept varchar(10) default 计算机 not …

【正点原子K210连载】第六十七章 音频FFT实验 摘自【正点原子】DNK210使用指南-CanMV版指南

第六十七章 音频FFT实验 本章将介绍CanMV下FFT的应用&#xff0c;通过将时域采集到的音频数据通过FFT为频域。通过本章的学习&#xff0c;读者将学习到CanMV下控制FFT加速器进行FFT的使用。 本章分为如下几个小节&#xff1a; 32.1 maix.FFT模块介绍 32.2 硬件设计 32.3 程序设…

基于 Ollama+Docker+OpenWebUI 的本地化部署deepseek流程

搭建deepseek 安装Ollama Ollama官方下载地址 下载完成后双击打开Ollama进行安装,点击install 安装完成后系统会弹出下图提示代表安装成功并且已启动 验证安装 ollama -v安装完成后&#xff0c;cmd 打开命令行窗口&#xff0c;输入 “ollama -v” 测试&#xff0c;显示 olla…

Mac 部署Ollama + OpenWebUI完全指南

文章目录 &#x1f4bb; 环境说明&#x1f6e0;️ Ollama安装配置1. 安装[Ollama](https://github.com/ollama/ollama)2. 启动Ollama3. 模型存储位置4. 配置 Ollama &#x1f310; OpenWebUI部署1. 安装Docker2. 部署[OpenWebUI](https://www.openwebui.com/)&#xff08;可视化…

C#常用集合优缺点对比

先上结论&#xff1a; 在C#中&#xff0c;链表、一维数组、字典、List<T>和ArrayList是常见的数据集合类型&#xff0c;它们各有优缺点&#xff0c;适用于不同的场景。以下是它们的比较&#xff1a; 1. 一维数组 (T[]) 优点&#xff1a; 性能高&#xff1a;数组在内存中…

额外题目汇总2-链表

链表 1.24. 两两交换链表中的节点 力扣题目链接(opens new window) 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 思路 使用虚拟头结点会很方便&#xff…

Nginx 中启用 Gzip 压缩以优化网页加载速度

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Nginx-从零开始的服务器之旅专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年2月7日17点14分 目录 1. 配置网页压缩 目的 …

《云夹:高效便捷的书签管理利器》

在信息爆炸的时代&#xff0c;我们每天都会浏览大量的网页&#xff0c;遇到许多有价值的内容。如何高效地管理这些网页书签&#xff0c;以便随时快速访问&#xff0c;成为了一个重要的问题。云夹作为一款出色的书签管理工具&#xff0c;为我们提供了完美的解决方案。 强大的功能…

学习数据结构(6)链表OJ

1.移除链表元素 解法一&#xff1a;&#xff08;我的做法&#xff09;在遍历的同时移除&#xff0c;代码写法比较复杂 解法二&#xff1a;创建新的链表&#xff0c;遍历原链表&#xff0c;将非val的节点尾插到新链表&#xff0c;注意&#xff0c;如果原链表结尾是val节点需要将…

MongoDB开发规范

分级名称定义P0核心系统需7*24不间断运行&#xff0c;一旦发生不可用&#xff0c;会直接影响核心业务的连续性&#xff0c;或影响公司名誉、品牌、集团战略、营销计划等&#xff0c;可能会造成P0-P2级事故发生。P1次核心系统这些系统降级或不可用&#xff0c;会间接影响用户使用…

设计模式.

设计模式 一、介绍二、六大原则1、单一职责原则&#xff08;Single Responsibility Principle, SRP&#xff09;2、开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;3、里氏替换原则&#xff08;Liskov Substitution Principle, LSP&#xff09;4、接口隔离原则&am…

STM32的HAL库开发-通用定时器输入捕获实验

一、通用定时器输入捕获部分框图介绍 1、捕获/比较通道的输入部分(通道1) 首先设置 TIM_CCMR1的CC1S[1:0]位&#xff0c;设置成01&#xff0c;那么IC1来自于TI1&#xff0c;也就是说连接到TI1FP1上边。设置成10&#xff0c;那个IC1来自于TI2&#xff0c;连接到TI2FP1上。设置成…