class083 动态规划中用观察优化枚举的技巧-下【算法】

class083 动态规划中用观察优化枚举的技巧-下【算法】

算法讲解083【必备】动态规划中用观察优化枚举的技巧-下

在这里插入图片描述

code1 1235. 规划兼职工作

// 规划兼职工作
// 你打算利用空闲时间来做兼职工作赚些零花钱,这里有n份兼职工作
// 每份工作预计从startTime[i]开始、endTime[i]结束,报酬为profit[i]
// 返回可以获得的最大报酬
// 注意,时间上出现重叠的 2 份工作不能同时进行
// 如果你选择的工作在时间X结束,那么你可以立刻进行在时间X开始的下一份工作
// 测试链接 : https://leetcode.cn/problems/maximum-profit-in-job-scheduling/

利用观察单调性 + 二分搜索的方式优化枚举,最优解时间复杂度O(n*logn)

dp[i]:[0…i]的最大报酬
不需要i位置的工作:dp[i-1]
需要i位置的工作:profit[i]+dp[j]:j是结束时间不超过startTime[i]的最右的

package class083;import java.util.Arrays;// 规划兼职工作
// 你打算利用空闲时间来做兼职工作赚些零花钱,这里有n份兼职工作
// 每份工作预计从startTime[i]开始、endTime[i]结束,报酬为profit[i]
// 返回可以获得的最大报酬
// 注意,时间上出现重叠的 2 份工作不能同时进行
// 如果你选择的工作在时间X结束,那么你可以立刻进行在时间X开始的下一份工作
// 测试链接 : https://leetcode.cn/problems/maximum-profit-in-job-scheduling/
public class Code01_MaximumProfitInJobScheduling {public static int MAXN = 50001;public static int[][] jobs = new int[MAXN][3];public static int[] dp = new int[MAXN];public static int jobScheduling(int[] startTime, int[] endTime, int[] profit) {int n = startTime.length;for (int i = 0; i < n; i++) {jobs[i][0] = startTime[i];jobs[i][1] = endTime[i];jobs[i][2] = profit[i];}// 工作按照结束时间从小到大排序Arrays.sort(jobs, 0, n, (a, b) -> a[1] - b[1]);dp[0] = jobs[0][2];for (int i = 1, start; i < n; i++) {start = jobs[i][0];dp[i] = jobs[i][2];if (jobs[0][1] <= start) {dp[i] += dp[find(i - 1, start)];}dp[i] = Math.max(dp[i], dp[i - 1]);}return dp[n - 1];}// job[0...i]范围上,找到结束时间 <= start,最右的下标public static int find(int i, int start) {int ans = 0;int l = 0;int r = i;int m;while (l <= r) {m = (l + r) / 2;if (jobs[m][1] <= start) {ans = m;l = m + 1;} else {r = m - 1;}}return ans;}}

code2 629. K 个逆序对数组

// K个逆序对数组
// 逆序对的定义如下:
// 对于数组nums的第i个和第j个元素
// 如果满足0<=i<j<nums.length 且 nums[i]>nums[j],则为一个逆序对
// 给你两个整数n和k,找出所有包含从1到n的数字
// 且恰好拥有k个逆序对的不同的数组的个数
// 由于答案可能很大,只需要返回对10^9+7取余的结果
// 测试链接 : https://leetcode.cn/problems/k-inverse-pairs-array/

最优解 利用观察 + 构造窗口累加和,已经进入 观察并设计高效的查询结构 的范畴了
只不过这个结构就仅存在于概念,并用一个int类型的变量维护而已

package class083;// K个逆序对数组
// 逆序对的定义如下:
// 对于数组nums的第i个和第j个元素
// 如果满足0<=i<j<nums.length 且 nums[i]>nums[j],则为一个逆序对
// 给你两个整数n和k,找出所有包含从1到n的数字
// 且恰好拥有k个逆序对的不同的数组的个数
// 由于答案可能很大,只需要返回对10^9+7取余的结果
// 测试链接 : https://leetcode.cn/problems/k-inverse-pairs-array/
public class Code02_KInversePairsArray {// 最普通的动态规划// 不优化枚举public static int kInversePairs1(int n, int k) {int mod = 1000000007;// dp[i][j] : 1、2、3...i这些数字,形成的排列一定要有j个逆序对,请问这样的排列有几种int[][] dp = new int[n + 1][k + 1];dp[0][0] = 1;for (int i = 1; i <= n; i++) {dp[i][0] = 1;for (int j = 1; j <= k; j++) {if (i > j) {for (int p = 0; p <= j; p++) {dp[i][j] = (dp[i][j] + dp[i - 1][p]) % mod;}} else {// i <= jfor (int p = j - i + 1; p <= j; p++) {dp[i][j] = (dp[i][j] + dp[i - 1][p]) % mod;}}}}return dp[n][k];}// 根据观察方法1优化枚举// 最优解// 其实可以进一步空间压缩// 有兴趣的同学自己试试吧public static int kInversePairs2(int n, int k) {int mod = 1000000007;int[][] dp = new int[n + 1][k + 1];dp[0][0] = 1;// window : 窗口的累加和for (int i = 1, window; i <= n; i++) {dp[i][0] = 1;window = 1;for (int j = 1; j <= k; j++) {if (i > j) {window = (window + dp[i - 1][j]) % mod;} else {// i <= jwindow = ((window + dp[i - 1][j]) % mod - dp[i - 1][j - i] + mod) % mod;}dp[i][j] = window;}}return dp[n][k];}}

code3 514. 自由之路

// 自由之路
// 题目描述比较多,打开链接查看
// 测试链接 : https://leetcode.cn/problems/freedom-trail/

优化枚举的核心是贪心策略:
下一步做枚举时,不需要枚举所有可能性,只需要枚举 顺时针的最近、逆时针的最近 两种可能性即可

贪心当然会有专题讲述!【必备】课程的动态规划专题结束了,就会开始贪心的专题

package class083;// 自由之路
// 题目描述比较多,打开链接查看
// 测试链接 : https://leetcode.cn/problems/freedom-trail/
public class Code03_FreedomTrail {// 为了让所有语言的同学都可以理解// 不会使用任何java语言自带的数据结构// 只使用最简单的数组结构public static int MAXN = 101;public static int MAXC = 26;public static int[] ring = new int[MAXN];public static int[] key = new int[MAXN];public static int[] size = new int[MAXC];public static int[][] where = new int[MAXC][MAXN];public static int[][] dp = new int[MAXN][MAXN];public static int n, m;public static void build(String r, String k) {for (int i = 0; i < MAXC; i++) {size[i] = 0;}n = r.length();m = k.length();for (int i = 0, v; i < n; i++) {v = r.charAt(i) - 'a';where[v][size[v]++] = i;ring[i] = v;}for (int i = 0; i < m; i++) {key[i] = k.charAt(i) - 'a';}for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {dp[i][j] = -1;}}}public static int findRotateSteps(String r, String k) {build(r, k);return f(0, 0);}// 指针当前指着轮盘i位置的字符,要搞定key[j....]所有字符,最小代价返回public static int f(int i, int j) {if (j == m) {// key长度是m// 都搞定return 0;}if (dp[i][j] != -1) {return dp[i][j];}int ans;if (ring[i] == key[j]) {// ring b//      i// key  b//      jans = 1 + f(i, j + 1);} else {// 轮盘处在i位置,ring[i] != key[j]// jump1 : 顺时针找到最近的key[j]字符在轮盘的什么位置// distance1 : 从i顺时针走向jump1有多远int jump1 = clock(i, key[j]);int distance1 = (jump1 > i ? (jump1 - i) : (n - i + jump1));// jump2 : 逆时针找到最近的key[j]字符在轮盘的什么位置// distance2 : 从i逆时针走向jump2有多远int jump2 = counterClock(i, key[j]);int distance2 = (i > jump2 ? (i - jump2) : (i + n - jump2));ans = Math.min(distance1 + f(jump1, j), distance2 + f(jump2, j));}dp[i][j] = ans;return ans;}// 从i开始,顺时针找到最近的v在轮盘的什么位置public static int clock(int i, int v) {int l = 0;// size[v] : 属于v这个字符的下标有几个int r = size[v] - 1, m;// sorted[0...size[v]-1]收集了所有的下标,并且有序int[] sorted = where[v];int find = -1;// 有序数组中,找>i尽量靠左的下标while (l <= r) {m = (l + r) / 2;if (sorted[m] > i) {find = m;r = m - 1;} else {l = m + 1;}}// 找到了就返回// 没找到,那i顺指针一定先走到最小的下标return find != -1 ? sorted[find] : sorted[0];}public static int counterClock(int i, int v) {int l = 0;int r = size[v] - 1, m;int[] sorted = where[v];int find = -1;// 有序数组中,找<i尽量靠右的下标while (l <= r) {m = (l + r) / 2;if (sorted[m] < i) {find = m;l = m + 1;} else {r = m - 1;}}// 找到了就返回// 没找到,那i逆指针一定先走到最大的下标return find != -1 ? sorted[find] : sorted[size[v] - 1];}}

code4 未排序数组中累加和小于或等于给定值的最长子数组长度

// 累加和不大于k的最长子数组
// 给定一个无序数组arr,长度为n,其中元素可能是正、负、0
// 给定一个整数k,求arr所有的子数组中累加和不大于k的最长子数组长度
// 要求时间复杂度为O(n)
// 测试链接 : https://www.nowcoder.com/practice/3473e545d6924077a4f7cbc850408ade
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

强烈推荐先看一下讲解046

利用构造单调数组 + 二分搜索的解不是最优解,时间复杂度O(n*logn)

最优解中包含的贪心思想(窗口的加速建立、可能性的淘汰),是这个题的重点,时间复杂度O(n)

贪心当然会有专题讲述!【必备】课程的动态规划专题结束了,就会开始贪心的专题

package class083;// 累加和不大于k的最长子数组
// 给定一个无序数组arr,长度为n,其中元素可能是正、负、0
// 给定一个整数k,求arr所有的子数组中累加和不大于k的最长子数组长度
// 要求时间复杂度为O(n)
// 测试链接 : https://www.nowcoder.com/practice/3473e545d6924077a4f7cbc850408ade
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;// 至今的最优解,全网题解几乎都是我几年前讲的方法
public class Code04_LongestSubarraySumNoMoreK {public static int MAXN = 100001;public static int[] nums = new int[MAXN];public static int[] minSums = new int[MAXN];public static int[] minSumEnds = new int[MAXN];public static int n, k;public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StreamTokenizer in = new StreamTokenizer(br);PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));while (in.nextToken() != StreamTokenizer.TT_EOF) {n = (int) in.nval;in.nextToken();k = (int) in.nval;for (int i = 0; i < n; i++) {in.nextToken();nums[i] = (int) in.nval;}out.println(compute2());}out.flush();out.close();br.close();}public static int compute1() {int[] sums = new int[n + 1];for (int i = 0, sum = 0; i < n; i++) {// sum : 0...i范围上,这前i+1个数字的累加和sum += nums[i];// sums[i + 1] : 前i+1个,包括一个数字也没有的时候,所有前缀和中的最大值sums[i + 1] = Math.max(sum, sums[i]);}int ans = 0;for (int i = 0, sum = 0, pre, len; i < n; i++) {sum += nums[i];pre = find(sums, sum - k);len = pre == -1 ? 0 : i - pre + 1;ans = Math.max(ans, len);}return ans;}public static int find(int[] sums, int num) {int l = 0;int r = n;int m = 0;int ans = -1;while (l <= r) {m = (l + r) / 2;if (sums[m] >= num) {ans = m;r = m - 1;} else {l = m + 1;}}return ans;}public static int compute2() {minSums[n - 1] = nums[n - 1];minSumEnds[n - 1] = n - 1;for (int i = n - 2; i >= 0; i--) {if (minSums[i + 1] < 0) {minSums[i] = nums[i] + minSums[i + 1];minSumEnds[i] = minSumEnds[i + 1];} else {minSums[i] = nums[i];minSumEnds[i] = i;}}int ans = 0;for (int i = 0, sum = 0, end = 0; i < n; i++) {while (end < n && sum + minSums[end] <= k) {sum += minSums[end];end = minSumEnds[end] + 1;}if (end > i) {// 如果end > i,// 窗口范围:i...end-1,那么窗口有效ans = Math.max(ans, end - i);sum -= nums[i];} else {// 如果end == i,那么说明窗口根本没扩出来,代表窗口无效// end来到i+1位置,然后i++了// 继续以新的i位置做开头去扩窗口end = i + 1;}}return ans;}}

2023-12-10 12:31:44

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

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

相关文章

GaussDB如何创建和管理视图

GaussDB如何创建和管理视图 一、什么是视图 当用户对数据库中的一张或者多张表的某些字段的组合感兴趣&#xff0c;而又不想每次键入这些查询时&#xff0c;用户就可以定义一个视图&#xff0c;以便解决这个问题。 视图与基本表不同&#xff0c;不是物理上实际存在的&#x…

基于ssm的“游侠”旅游信息管理系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本“游侠”旅游信息管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的…

自动灭火贴哪个牌子好?看懂灭火贴原理应该这么选!

随着大家安全意识的增强&#xff0c;无论是日常生活还是工业生产&#xff0c;都可以看到灭火器的踪影。但是往往在火情中&#xff0c;人们很难第一时间发现危险并及时扑灭&#xff0c;或多或少会造成财产损失&#xff0c;甚至影响到生命安全。近几年&#xff0c;自动灭火贴应运…

第十五章 React使用UI(Ant Design)框架

一、专栏介绍 &#x1f606;&#x1f606; 欢迎加入本专栏&#xff01;本专栏将引领您快速上手React&#xff0c;让我们一起放弃放弃的念头&#xff0c;开始学习之旅吧&#xff01;我们将从搭建React项目开始&#xff0c;逐步深入讲解最核心的hooks&#xff0c;以及React路由、…

【论文翻译】Learning Deep Features for Discriminative Localization

原文&#xff1a;Learning Deep Features for Discriminative Localization 摘要 在这项工作中&#xff0c;我们重新审视了文献[13]中提出的全局平均池化层&#xff0c;并阐明了它如何明确地使卷积神经网络具有出色的定位能力&#xff0c;尽管该网络是在图像级标签上进行训练的…

排序算法:【冒泡排序】、逻辑运算符not用法、解释if not tag:

注意&#xff1a; 1、排序&#xff1a;将一组无序序列&#xff0c;调整为有序的序列。所谓有序&#xff0c;就是说&#xff0c;要么升序要么降序。 2、列表排序&#xff1a;将无序列表变成有序列表。 3、列表这个类里&#xff0c;内置排序方法&#xff1a;sort( )&#xff0…

Python实现图形的几何变换

一、能够提供对话框窗口&#xff0c;输入三角形的顶点坐标&#xff1b;或者&#xff0c;采用鼠标左键单击方式输入三角形的顶点坐标。 二、对输入的三角形顶点坐标进行五种基本几何变换&#xff1a; 对于平移变换&#xff0c;能够提供对话框窗口&#xff0c;输入平移向量&…

HashMap:理解Hash、底层实现与扩容机制

一、简单叙述 HashMap是Java中常用的一种数据结构&#xff0c;它以键值对的形式存储数据&#xff0c;具有高效的查找、插入和删除操作。本文将详细介绍HashMap的底层实现原理&#xff0c;包括哈希技术、底层数据结构和扩容机制&#xff0c;帮助读者深入理解HashMap的工作原理。…

Java IO 模型之 BIO、NIO、AIO 详解

目录 一. 前言 二. IO 模型 2.1. IO 模型分类 2.2. BIO、NIO、AIO 使用场景分析 2.3. NIO 和 BIO 的比较 三. BIO&#xff08;同步阻塞&#xff09; 3.1. BIO 编程流程 3.2. BIO 应用实例 3.3. 问题分析 四. NIO&#xff08;同步非阻塞&#xff09; 4.1. 基本介绍 …

Dubbo 3.x源码(13)—Dubbo服务发布导出源码(2)

基于Dubbo 3.1&#xff0c;详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)&#xff0c;也就是Dubbo服务发布导出的入口源码&#xff0c;现在我们继续学习&#xff0c;服务导出的核心方法doExportUrls的源码。 Dubbo 3.x…

一文读懂Java中的设计模式——单例模式!默认情况下,Spring的Bean就是单例的

单例模式概念 单例模式确保某一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供这个实例。单例模式只应在有真正的“单一实例”的需求时才可使用。总结起来单例模式有三个优点&#xff1a; 全局访问共享资源&#xff1a;当需要在应用程序的多个地方共享和使用相…

跑代码中遇到的错误合集(持续更新)

1.TypeError: dropout(): argument ‘input‘ (position 1) must be Tensor, not str 原因&#xff1a;dropout函数接收到的参数是一个字典类型(需手动设置其不要返回字典类型) 解决步骤: 1.根据代码定位到dropout函数 2.定位到函数中的参数 3.对给dropout函数参数赋值的函数的…

[渗透测试学习] Sau - HackTheBox

首先是信息搜集&#xff0c;nmap扫一下 nmap -sV -sC -p- -v 10.10.11.224 发现存在两个端口&#xff0c;55555端口有http服务&#xff0c;访问一下 获得线索request-baskets版本为1.2.1&#xff0c;搜索发现存在漏洞 那么我们试试构造ssrf&#xff0c;create的时候bp抓包 构…

定时器TIM HAL库+cubeMX(上)

定时器时钟源APB1 36MHz 一.基本定时器 1.基本框图 2.溢出时间计算 3.配置定时器步骤 TIM_HandleTypeDef g_timx_handle;/* 定时器中断初始化函数 */ void btim_timx_int_init(uint16_t arr, uint16_t psc) {g_timx_handle.Instance TIM6;g_timx_handle.Init.Prescaler p…

【数据安全】金融行业数据安全保障措施汇总

数字化的今天&#xff0c;数据的价值不可估量&#xff0c;尤其是金融行业&#xff0c;数据不仅代表着企业的核心资产&#xff0c;还涉及到客户的隐私和信任。因此对于金融行业而言&#xff0c;保障数据安全至关重要。下面我们就来一起讨论为什么金融行业要保障数据安全&#xf…

Idea执行bat使用maven打包springboot项目成docker镜像并push到Harbor

如果执行以下命令失败&#xff0c;先把mvn的-q参数去掉&#xff0c;让错误输出到控制台。 《idea配置优化、Maven配置镜像、并行构建加速打包、解决maven打包时偶尔几个文件没权限的问题》下面的使用company-repo私有仓库和阿里云镜像仓库同时使用的配置参考。 bat echo off …

要实现长页面滑动到指定位置触发动画效果(亲测有效)

1.添加触发动画的元素&#xff1a;在你的 HTML 文件中&#xff0c;将需要触发动画的元素添加相应的类名<div class"animation"> <p>安全工矿 智能工矿 安全工矿 智能工矿</p> </div> 给一个 <div> 元素添加 .animation…

JVM 详解(JVM组成部分、双亲委派机制、垃圾回收算法、回收器、回收类型、了解调优思路)

目录 JVM 详解&#xff08;JVM组成部分、双亲委派机制、垃圾回收算法、回收器、回收类型、了解调优思路&#xff09;1、概念&#xff1a;什么是 JVM ?JVM 的作用&#xff1f; 2、JVM 的主要组成部分&#xff1f;类加载器&#xff08;Class Loader&#xff09;&#xff1a;简单…

02基于matlab的卡尔曼滤波

基于matlab的卡尔曼滤波&#xff0c;可更改状态转移方程&#xff0c;控制输入&#xff0c;观测方程&#xff0c;设置生成的信号的噪声标准差&#xff0c;设置状态转移方差Q和观测方差R等参数&#xff0c;程序已调通&#xff0c;需要直接拍下。

Vue学习计划-Vue2--VueCLi(五)全局事件总线、消息订阅与发布(pubsub)

抛出问题:我们多级组件&#xff0c;或者任意不想关的子组件如何传递数据呢&#xff1f; 1. 全局事件总线&#xff08;$bus&#xff09; 一种组件间通信的方式&#xff0c;适用于任意组件间通信 全局事件总线示意图&#xff1a; 安装全局事件总线&#xff1a; new Vue({..…