【LeetCode】动态规划—1964. 找出到每个位置为止最长的有效障碍赛跑路线(附完整Python/C++代码)

ops/2024/10/15 19:31:11/

动态规划—1964. 找出到每个位置为止最长的有效障碍赛跑路线

  • 前言
  • 题目描述
  • 基本思路
    • 1. 问题定义
    • 2. 理解问题和递推关系
      • 动态规划递推公式:
      • 公式推导:
      • 伪代码:
      • 核心思想:
    • 3. 解决方法
    • 4. 进一步优化
    • 5. 小总结
  • Python代码
      • Python代码解释总结:
  • C++代码
      • C++代码解释总结:
  • 总结

前言

最长有效障碍物路线问题 是一个涉及到 最长递增子序列(LIS) 变种的问题。给定一个障碍物高度的数组,要求在每个位置找到一个最长有效的障碍物路线,满足在该位置的障碍物高度必须大于或等于之前路线中的最大高度。这个问题结合了 动态规划二分查找 的思想,我们可以使用这些算法来高效地求解该问题。


题目描述

在这里插入图片描述

基本思路

1. 问题定义

给定一个数组 obstacles,数组中每个元素表示障碍物的高度。对于每个位置 i,我们需要找到一个最长的障碍物路线(到当前位置为止),满足路线中每个位置的障碍物高度都不超过当前位置的障碍物高度。

2. 理解问题和递推关系

这个问题本质上是一个变种的 LIS(最长递增子序列) 问题,但与传统的 LIS 不同的是,这里允许相等的元素。要求在每个位置 i 处计算到该位置的最长有效障碍物路线长度。可以用类似 LIS 的方式解决该问题,并利用 二分查找 优化。

动态规划递推公式:

  • 定义一个数组 dp,其中 dp[i] 表示从第一个位置到第 i 个位置的最长有效障碍物路线的长度。
  • tails 数组用于维护当前最长有效障碍物路线的最小结尾值。对于每个 obstacles[i],我们在 tails 中找到最右边的一个值,使得其小于等于 obstacles[i],然后更新这个值或扩展 tails 数组。

公式推导:

  1. 初始化一个空的 tails 数组,用于存储障碍物的递增序列的结尾。
  2. 对于每一个 obstacles[i]
    • 使用二分查找找到 tails 中第一个大于 obstacles[i] 的位置 pos
    • 如果 pos 是有效的索引,则替换 tails[pos]obstacles[i]
    • 如果 pos 超出了 tails 的长度,则将 obstacles[i] 追加到 tails 的末尾。
    • 更新 dp[i] = pos + 1,表示在位置 i 处的最长障碍物路线长度。

伪代码:

initialize empty tails array
for each obstacle in obstacles:find position pos using binary search where tails[pos] > obstacleif pos is valid:update tails[pos] with current obstacleelse:append obstacle to tailsset dp[i] = pos + 1
return dp array

核心思想:

  • 通过维护一个 tails 数组,来跟踪有效的障碍物路线,使用二分查找优化搜索位置。
  • dp[i] 记录每个位置的最长有效障碍物路线的长度。

3. 解决方法

动态规划 + 二分查找

  1. 初始化一个空的 tails 数组,用于存储当前的有效障碍物路线。
  2. 对每一个 obstacles[i],通过二分查找找到可以替换的位置或追加的位置,然后更新 dp[i]
  3. 最终 dp 数组即为每个位置的最长有效障碍物路线。

4. 进一步优化

通过使用二分查找,动态维护 tails 数组的最小结尾值,可以将动态规划的时间复杂度从 O(n^2) 降低到 O(n log n)。这种优化适用于处理大规模数据。

5. 小总结

  • 本问题的核心思想是利用 LIS 的变种来解决障碍物路线问题。
  • 通过动态规划和二分查找的结合,可以在 O(n log n) 的时间复杂度内高效地找到每个位置的最长有效障碍物路线。

以上就是找出到每个位置为止最长的有效障碍赛跑路线问题的基本思路。


Python代码

python">class Solution:def longestObstacleCourseAtEachPosition(self, obstacles: list[int]) -> list[int]:tails = []  # tails用于存储递增序列的末尾值dp = []  # dp用于记录每个位置的最长有效路线长度for i, obstacle in enumerate(obstacles):# 找到可以插入的位置,使用bisect_right允许相等的元素pos = bisect_right(tails, obstacle)if pos < len(tails):tails[pos] = obstacle  # 替换该位置的值else:tails.append(obstacle)  # 如果没有合适位置,则追加dp.append(pos + 1)  # dp[i]表示以obstacles[i]结尾的最长有效路线return dp

Python代码解释总结:

  1. 二分查找:通过 bisect_right 找到 tails 数组中第一个大于当前 obstacle 的位置。
  2. 更新 tails 数组:如果找到位置 pos,则更新该位置的值为当前障碍物高度;如果没找到位置,则将当前障碍物高度追加到 tails 数组末尾。
  3. 返回 dp 数组dp[i] 表示在每个位置的最长有效障碍物路线长度,最终返回 dp 数组。

C++代码

class Solution {
public:vector<int> longestObstacleCourseAtEachPosition(vector<int>& obstacles) {vector<int> tails;  // tails用于存储递增序列的末尾值vector<int> dp;     // dp用于存储结果for (int obstacle : obstacles) {// 使用lower_bound找到第一个大于当前障碍物的元素位置int pos = upper_bound(tails.begin(), tails.end(), obstacle) - tails.begin();if (pos < tails.size()) {tails[pos] = obstacle;  // 替换该位置的值} else {tails.push_back(obstacle);  // 如果没有合适的位置,追加当前障碍物}dp.push_back(pos + 1);  // dp[i]表示在第i个位置的最长有效路线长度}return dp;}
};

C++代码解释总结:

  1. 二分查找:通过 upper_bound 找到 tails 数组中第一个大于当前 obstacle 的位置。
  2. 更新 tails 数组:如果找到位置 pos,则更新该位置的值;否则将当前 obstacle 追加到 tails 数组末尾。
  3. 返回 dp 数组dp[i] 表示在每个位置的最长有效障碍物路线长度,最终返回结果。

总结

  • 核心思想:最长有效障碍物路线问题通过将障碍物的高度序列转化为 最长递增子序列(LIS) 的变种问题,可以通过动态规划和二分查找来高效求解。
  • 时间复杂度:通过使用二分查找维护 tails 数组,时间复杂度为 O(n log n),适合处理大规模数据。
  • 解决方案:本文详细介绍了 Python 和 C++ 实现,展示了如何通过二分查找和动态规划来高效地找到每个位置的最长有效障碍物路线长度。

http://www.ppmy.cn/ops/126079.html

相关文章

ASP.NET Core8.0学习笔记(二十一)——EFCore关系配置API

一、关系配置API概述 当我们需要指定一个字段作为外键&#xff0c;而这个外键又不符合以上四种约定时&#xff0c;就需要在IEntityTypeConfiguration实现类&#xff08;对应的配置类&#xff09;中使用Fluent API直接配置外键。理论上可以通过API直接指定一个属性&#xff0c;…

GC 算法

垃圾回收主要在&#xff1a;堆区和方法区 # 标记阶段&#xff1a;引用计数算法 在堆里存放着几乎所有的 Java 对象实例&#xff0c;在 GC 执行垃圾回收之前&#xff0c;首先需要区分出内存中哪些是存活对象&#xff0c;哪些是已经死亡的对象。只有被标记为已经死亡的对象&…

专题:贪心算法(已完结)

1.分发饼干 方法一&#xff1a;用最大的胃口 找到最大的饼干&#xff08;先遍历胃口&#xff09; class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {// 主要思路 用最大的饼干找最大的胃口sort(g.begin(),g.end());so…

YoloDotNet 在工业检测中的应用详解

文章目录 一、数据收集与标注二、模型选择与训练三、检测流程设计四、结果评估与优化五、与工业生产线集成一、数据收集与标注 在工业检测中,首先需要收集大量的相关工业产品图像数据。这些数据应涵盖不同的产品类型、缺陷种类以及各种可能的生产状态。例如,对于电子产品的检…

MySQL安装与配置详细教程

MySQL 是一个广泛使用的开源数据库管理系统&#xff0c;适用于各类应用程序。本文将详细介绍如何在 Linux&#xff08;如 Ubuntu&#xff09;、Windows 系统上安装和配置 MySQL&#xff0c;以帮助你快速搭建起一个稳定的数据库环境。 一、在 Ubuntu 系统上安装 MySQL 1. 更新…

国产人工智能教学实验箱操作案例分享:5-27 指纹识别实验

一、实验目的 熟悉Qt程序的开发流程。 掌握Qt Creator的基础开发使用。 通过编写Qt程序实现指纹识别的显示界面。 二、实验原理 Qt工程的创建步骤包括&#xff1a; &#xff08;1&#xff09;创建Qt工程&#xff1b; &#xff08;2&#xff09;GUI的设计实现&#xff1a;LCD…

出海快报 | “三消+短剧”手游横空出世,黄油相机“出圈”日本市场,从Q1看日本手游市场趋势和机会

编者按&#xff1a;TopOn出海快报栏目为互联网出海从业者梳理出海热点&#xff0c;供大家了解行业最新发展态势。 1.“三消短剧”横空出世&#xff0c;融合创新手游表现亮眼 随着竞争的加剧&#xff0c;新产品想要突出重围&#xff0c;只能在游戏中加入额外的元素。第一次打开…

萱仔求职复习系列——2 Linux的常用方法(包含基础进阶高级操作)

由于最近接了一个笔试&#xff0c;发现笔试可能涉及到Linux&#xff0c;我准备临时抱佛脚一下赶紧复习一下Linux的用法哈哈。Linux 的基础用法包含文件系统操作、权限管理、网络配置、进程管理等基本命令&#xff1b;进阶操作包括网络调试、包管理、服务管理和用户管理等&#…