背景

基于例题《任务安排》逐步推导进行斜率优化。

引入

例题:P2365 任务安排

考虑动态规划。使用 d p i , j dp_{i,j} dpi,j 表示前 i i i 个任务分了 j j j 段的最小费用。

显然,有 d p i , j = min ⁡ k = 1 i − 1 ( d p i , j , d p k , j − 1 + ( t o t i − t o t k ) ) ∗ ( s u m [ i ] + s ∗ j ) ) dp_{i,j} = \min_{k=1}^{i-1} (dp_{i,j},dp_{k,j-1} + (tot_i-tot_k))*(sum[i]+s*j)) dpi,j=mink=1i1(dpi,j,dpk,j1+(totitotk))(sum[i]+sj))​ 。

  • s u m i sum_i sumi 表示 c i c_i ci 的前缀和。
  • t o t i tot_i toti 表示 t i t_i ti 的前缀和。

前缀和优化后,时间复杂度 O ( n 3 ) O(n^3) O(n3),得到 60pts.

代码

#include <bits/stdc++.h>
using namespace std;
int n,s,ans,t[5005],c[5005],dp[5005][5005],sum[5005],tot[5005];
int main()
{cin >> n >> s;for (int i=1;i<=n;i++){cin >> t[i] >> c[i];sum[i] = sum[i-1] + t[i];tot[i] = tot[i-1] + c[i];}memset(dp,0x3f,sizeof(dp));ans = 0x3f3f3f3f;dp[0][0] = 0;for (int i=1;i<=n;i++){for (int j=1;j<=i;j++){for (int k=0;k<i;k++){dp[i][j] = min(dp[i][j],dp[k][j-1] + (tot[i]-tot[k])*(sum[i]+s*j));}	}	}for (int i=1;i<=n;i++){ans = min(ans,dp[n][i]);}cout<<ans;return 0;
}

如何进一步优化呢?

我们发现,可以把有关 s s s 的计算在前面完成。也就是 费用提前计算 ,就不需要枚举分的段数了。

得到状态转移方程 d p i = min ⁡ ( d p i , d p j + s u m i ∗ t o t i − s u m i ∗ t o t j + t o t n ∗ s − t o t j ∗ s ) dp_i = \min(dp_i,dp_j + sum_i*tot_i-sum_i*tot_j + tot_n*s-tot_j*s) dpi=min(dpi,dpj+sumitotisumitotj+totnstotjs)

代码

#include <bits/stdc++.h>
using namespace std;
long long n,s,ans,t[5005],c[5005],dp[5005],sum[5005],tot[5005];
int main()
{cin >> n >> s;for (int i=1;i<=n;i++){cin >> t[i] >> c[i];sum[i] = sum[i-1] + t[i];tot[i] = tot[i-1] + c[i];dp[i] = 1e18;}ans = 1e18;dp[0] = 0;for (int i=1;i<=n;i++){for (int j=0;j<i;j++){dp[i] = min(dp[i],dp[j] + sum[i]*(tot[i]-tot[j]) + (tot[n]-tot[j])*s);}	}cout<<dp[n];return 0;
}

正文

状态转移方程 d p i = min ⁡ ( d p i , d p j + s u m i ∗ t o t i − s u m i ∗ t o t j + t o t n ∗ s − t o t j ∗ s ) dp_i = \min(dp_i,dp_j + sum_i*tot_i-sum_i*tot_j + tot_n*s-tot_j*s) dpi=min(dpi,dpj+sumitotisumitotj+totnstotjs)

把与 i , j i,j i,j 有关的各单独放在一起,得到 d p i = min ⁡ ( d p i , d p j + s u m i ∗ t o t i + t o t n ∗ s − t o t j ∗ ( s u m i + s ) ) dp_i = \min(dp_i,dp_j + sum_i*tot_i + tot_n*s - tot_j*(sum_i+s)) dpi=min(dpi,dpj+sumitoti+totnstotj(sumi+s))

去掉最小值,得到 d p i = d p j + s u m i ∗ t o t i + t o t n ∗ s − t o t j ∗ ( s u m i + s ) dp_i = dp_j + sum_i*tot_i + tot_n*s - tot_j*(sum_i+s) dpi=dpj+sumitoti+totnstotj(sumi+s)

移项,得到 d p j = t o t j ∗ ( s u m i + s ) + d p i − s u m i ∗ t o t i − t o t n ∗ s dp_j = tot_j*(sum_i+s) + dp_i - sum_i*tot_i - tot_n*s dpj=totj(sumi+s)+dpisumitotitotns

t o t j tot_j totj 为横坐标, d p j dp_j dpj 为纵坐标的平面直角坐标系中,

这是一条 y = ( s + s u m i ) ∗ x + d p i − s u m i ∗ t o t i − t o t n ∗ s y = (s+sum_i) * x + dp_i - sum_i * tot_i - tot_n * s y=(s+sumi)x+dpisumitotitotns 的直线。

写成 y = k x + b y = kx+b y=kx+b 的形式, k = s + s u m i k = s+sum_i k=s+sumi b = d p i − s u m i ∗ t o t i − t o t n ∗ s b = dp_i-sum_i*tot_i-tot_n*s b=dpisumitotitotns.

由于 k k k 是定值,所求的 d p i dp_i dpi 存在于 b b b 中,所以我们只需要找到最小的 b b b 即可。


如何寻找最小的 b b b

发现有一些点会出现在这条直线上,我们把这样的点称为 决策点,即 ( t o t j , d p j ) (tot_j,dp_j) (totj,dpj)

对于这些决策点,由于 k k k 是定值,所以有且只有一条 k = s + s u m i k=s+sum_i k=s+sumi 的直线经过一个决策点,这些决策点一共会产生不超过 j j j 条直线。

对于已知的一个决策点 ( t o t j , d p j ) (tot_j,dp_j) (totj,dpj),我们把它们带入到一次函数表达式里去,就能解出一个 b b b ,枚举 j j j 得到最小的 b b b 即可。

但这种方法过于朴素,时间复杂度不变。考虑优化。

由于我们是从决策点出发,推导 b b b 的值。则说明决策点坐标(或者说 j j j )与 b b b 之间存在线性关系。考虑决策点坐标之间的关系来优化。

对于三个决策点 ( t o t j 1 , d p j 1 ) , ( t o t j 2 , d p j 2 ) , ( t o t j 3 , d p j 3 ) (tot_{j_1},dp_{j_1}),(tot_{j_2},dp_{j_2}),(tot_{j_3},dp_{j_3}) (totj1,dpj1),(totj2,dpj2),(totj3,dpj3) (我们设这三点 j 1 < j 2 < j 3 j_1 < j_2 < j_3 j1<j2<j3 ,由于 t , c > 0 t,c > 0 t,c>0 ,所以这三点的横坐标依次递增,即 t o t j 1 < t o t j 2 < t o t j 3 tot_{j_1} < tot_{j_2} < tot_{j_3} totj1<totj2<totj3)来说,当这三个决策点有且仅有取 ( t o t j 2 , d p j 2 ) (tot_{j_2},dp_{j_2}) (totj2,dpj2) 时, b b b 有最小值,那么这三点所构成的直线不会两两重合,并分为两种情况:

情况 1 ( j 2 j_2 j2 j 1 j_1 j1 j 3 j_3 j3 的连线上方)

当这三点构成一个向上凸出的形状,即 上凸 。显然此时 j 2 j_2 j2 一定不会使得 b b b 取最小值,如下图所示。

image.png

情况 2 ( j 2 j_2 j2 j 1 j_1 j1 j 3 j_3 j3 的连线下方)

当这三点构成一个向下凸出的形状,即 下凸 。显然此时 j 2 j_2 j2 可能使得 b b b 取最小值,如下图所示。

image.png

发现只有 下凸 的情况 ( j 2 j_2 j2 j 1 j_1 j1 j 3 j_3 j3 的连线下方) 才可能使 j 2 j_2 j2 取到最小的 b b b

则有 d p j 2 − d p j 1 t o t j 2 − t o t j 1 < d p j 3 − d p j 2 t o t j 3 − t o t j 2 \frac{dp_{j_2}-dp_{j_1}}{tot_{j_2}-tot_{j_1}} < \frac{dp_{j_3}-dp_{j_2}}{tot_{j_3}-tot_{j_2}} totj2totj1dpj2dpj1<totj3totj2dpj3dpj2

即直线 j 1 → j 2 j_1 \to j_2 j1j2 k k k 小于 j 2 → j 3 j_2 \to j_3 j2j3 直线的 k k k ,本质上是这两条直线的斜率关系。


因此,我们需要维护 相邻两点间直线的 k k k (斜率) ,并当它们 单调递增 时, j 2 j_2 j2 所得到的 b b b 就可能是最小值。

那么什么时候 j 2 j_2 j2 所取的 b b b 就一定是最小值呢?

image.png

我们发现,当一段单调递增的 k k k 满足一个点的左边的 k ’ k’ k 都小于 k k k ,右边的 k ’ k’ k 都大于 k k k 时,这个点就是使 b b b 最小的点。

如果我们只维护 相邻两点间连线斜率大于等于 k k k k ′ k' k (斜率),那么在这个单调递增的序列中最小值就能使 b b b 最小。

这不就是单调队列的思路吗?

总结一下:

  1. 我们用单调队列维护相邻两点间直线的 k k k ,使其单调递增。
  2. 在单调队列里放的是 k k k 单调递增的点的编号。
  3. 最终答案是单调队列的队头坐标代入 d p i = d p j + s u m i ∗ t o t i + t o t n ∗ s − t o t j ∗ ( s u m i + s ) dp_i = dp_j + sum_i*tot_i + tot_n*s - tot_j*(sum_i+s) dpi=dpj+sumitoti+totnstotj(sumi+s).
  4. 为了维护单调性,我们需要从左侧队头开始删除。即判断队头斜率 d p q h e a d + 1 − d p q h e a d t o t q h e a d + 1 − t o t q h e a d ≤ s + s u m i \frac{dp_{q_{head+1}}-dp_{q_{head}}}{tot_{q_{head+1}}-tot_{q_{head}}} \leq s+sum_i totqhead+1totqheaddpqhead+1dpqheads+sumi 时,把队头出队即可。 为了避免精度问题,且 t o t tot tot 有单调递增性,那么我们不妨判断 d p q h e a d + 1 − d p q h e a d ≤ ( s + s u m i ) ∗ ( t o t q h e a d + 1 − t o t q h e a d ) {dp_{q_{head+1}}-dp_{q_{head}}} \leq (s+sum_i) * ({tot_{q_{head+1}}-tot_{q_{head}}}) dpqhead+1dpqhead(s+sumi)(totqhead+1totqhead).
  5. 添加时,如果 q i q_i qi 不能与前面的点满足单调性,那么直接把前面的点不断出队,直到满足单调性为止。即当 d p i − d p q t a i l t o t i − t o t q t a i l ≤ d p q t a i l − d p q t a i l − 1 t o t q t a i l − t o t q t a i l − 1 \frac{dp_{i}-dp_{q_{tail}}}{tot_{i}-tot_{q_{tail}}} \leq \frac{dp_{q_{tail}}-dp_{q_{tail-1}}}{tot_{q_{tail}}-tot_{q_{tail-1}}} totitotqtaildpidpqtailtotqtailtotqtail1dpqtaildpqtail1 时不断出队即可。同样避免精度问题,判断 ( d p i − d p q t a i l ) ∗ ( t o t q t a i l − t o t q t a i l − 1 ) ≤ ( d p q t a i l − d p q t a i l − 1 ) ∗ ( t o t i − t o t q t a i l ) ({dp_{i}-dp_{q_{tail}}}) * ({tot_{q_{tail}}-tot_{q_{tail-1}}}) \leq ({dp_{q_{tail}}-dp_{q_{tail-1}}})*({tot_{i}-tot_{q_{tail}}}) (dpidpqtail)(totqtailtotqtail1)(dpqtaildpqtail1)(totitotqtail) 即可。

时间复杂度 O ( n ) O(n) O(n) .

#include <bits/stdc++.h>
using namespace std;
const int N = 300005;
long long n,s,ans,t[N],c[N],dp[N],sum[N],tot[N];
long long q[N],head=1,tail=1;
int main()
{cin >> n >> s;for (int i=1;i<=n;i++){cin >> t[i] >> c[i];sum[i] = sum[i-1] + t[i];tot[i] = tot[i-1] + c[i];dp[i] = 1e18;}ans = 1e18;dp[0] = 0;for (int i=1;i<=n;i++){while (head < tail && dp[q[head+1]]-dp[q[head]] <= (s+sum[i])*(tot[q[head+1]]-tot[q[head]])) head++;dp[i] = dp[q[head]] + sum[i]*tot[i] + tot[n]*s - tot[q[head]]*(sum[i]+s);while (head < tail && (dp[i]-dp[q[tail]])*(tot[q[tail]]-tot[q[tail-1]]) <= (dp[q[tail]]-dp[q[tail-1]])*(tot[i]-tot[q[tail]])) tail--;q[++tail] = i;}cout<<dp[n];return 0;
}

为什么单调队列初始 head = 1,tail = 1 ,而不能写作 head = 1,tail = 0 ?
考虑到还有 dp[0] = 0 ,要么把 tail 设为 head ,要么把 tail 设为 head-1 再在队列里加入 dp[0]

后记

斜率优化看起来好像的确比想象中抽象很多,希望对大家理解斜率优化有所帮助!

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

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

相关文章

MySQL中实现大数据量的快速插入

一、SQL语句优化​ 1. ​批量插入代替单条插入​ ​单条插入会频繁触发事务提交和日志写入&#xff0c;效率极低。​批量插入通过合并多条数据为一条SQL语句&#xff0c;减少网络传输和SQL解析开销。 -- 低效写法&#xff1a;逐条插入 INSERT INTO table (col1, col2) VALUE…

C++23中std::span和std::basic_string_view可平凡复制提案解析

文章目录 一、引言二、相关概念解释2.1 平凡复制&#xff08;Trivially Copyable&#xff09;2.2 std::span2.3 std::basic_string_view 三、std::span和std::basic_string_view的应用场景3.1 std::span的应用场景3.2 std::basic_string_view的应用场景 四、P2251R1提案对std::…

广东省省考备考(第十八天5.23)—言语:语句填空题(听课后强化训练)

错题 解析 横线出现在文段中间&#xff0c;需结合上下文内容进行分析。文段开篇指出逃离北上广深的话题时而出现&#xff0c;一些人离开大城市回到小城市。随后通过转折词“但”引出横线内容&#xff0c;且结合横线后人才倾向于向更发达的地方流动的内容&#xff0c;横线处应体…

持续更新 ,GPT-4o 风格提示词案例大全!附使用方式

本文汇集了各类4o风格提示词的精选案例&#xff0c;从基础指令到复杂任务&#xff0c;从创意写作到专业领域&#xff0c;为您提供全方位的参考和灵感。我们将持续更新这份案例集&#xff0c;确保您始终能够获取最新、最有效的提示词技巧。 让我们一起探索如何通过精心设计的提…

创建型:建造者模式

目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 工作流程 2.3 实现案例 2.4 变体&#xff1a;链式建造者&#xff08;常见于多参数对象&#xff0c;无需指挥者&#xff09; 3、优缺点分析 4、适用场景 1、核心思想 目的&#xff1a;将复杂对象的构建过程与其表示分离…

力扣-长度最小的子数组

1.题目描述 2.题目链接 LCR 008. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 3.题目分析 这道题目我们使用的也是双指针。我们可以定义两个指针都指向数组第一个元素&#xff0c;然后使用right指针遍历原数组&#xff0c;计算left指针到right指针之间的所有元…

JAVA开发工具延长方案

亲测稳定的延长方案与避坑指南 真的搞不懂了&#xff0c;说点专业的术语竟然成了 QINQUAN。那就直接点&#xff0c;把这个方案带给需要的开发者。 延长工具直通车 保姆级教程 延长方案https://mp.weixin.qq.com/s/uajM2Y9Vz6TnolzcLur_bw还是让大家看看&#xff0c;发什么会被…

SpringAI开发SSE传输协议的MCP Server

SpringAI 访问地址&#xff1a;Spring AI ‌ Spring AI‌是一个面向人工智能工程的应用框架&#xff0c;由Spring团队推出&#xff0c;旨在将AI能力集成到Java应用中。Spring AI的核心是解决AI集成的根本挑战&#xff0c;即将企业数据和API与AI模型连接起来‌。 MCP…

JAVA动态生成类

在java的加载过程一般都是要预先定义java类,然后通过经过加载->连接->初始化三步。连接过程又可分为三步:验证->准备->解析。初始化的类是不允许修改。但是在日常的工作中有时候需要动态生成类,那第这种情况怎么办呢? 可以这么处理: 1、先定义一个空的类,仅…

深入解析Java微服务架构:Spring Boot与Spring Cloud的整合实践

深入解析Java微服务架构&#xff1a;Spring Boot与Spring Cloud的整合实践 引言 随着云计算和分布式系统的快速发展&#xff0c;微服务架构已成为现代软件开发的主流模式。Java作为企业级应用开发的核心语言&#xff0c;结合Spring Boot和Spring Cloud&#xff0c;为开发者提…

03_基础篇-NumPy(下):深度学习中的常用操作

03_基础篇-NumPy&#xff08;下&#xff09;&#xff1a;深度学习中的常用操作 通过上节课的学习&#xff0c;我们已经对NumPy数组有了一定的了解&#xff0c;正所谓实践出真知&#xff0c;今天我们就以一个图像分类的项目为例&#xff0c;看看NumPy的在实际项目中都有哪些重要…

时钟识别项目报告(深度学习、计算机视觉)

深度学习方式 一、模型架构 本模型采用双任务学习框架&#xff0c;基于经典残差网络实现时钟图像的小时和分钟同步识别。 主干网络 使用预训练的ResNet18作为特征提取器&#xff0c;移除原分类层&#xff08;fc层&#xff09;&#xff0c;保留全局平均池化后的512维特征向量。…

openai-whisper-asr-webservice接入dify

openai-whisper-asr-webservice提供的asr的api其实并不兼容openai的api&#xff0c;所以在dify中是不能直接添加到语音转文字的模型中&#xff0c;对比了下两个api的传参情况&#xff0c;其实只要改动一处&#xff0c;就能支持&#xff1a; openai兼容的asr调用中formdata中音频…

解锁MySQL性能调优:高级SQL技巧实战指南

高级SQL技巧&#xff1a;解锁MySQL性能调优的终极指南 开篇 当前&#xff0c;随着业务系统的复杂化和数据量的爆炸式增长&#xff0c;数据库性能调优成为了技术人员面临的核心挑战之一。尤其是在高并发、大数据量的场景下&#xff0c;SQL 查询的性能直接影响到整个系统的响应…

JavaScript 性能优化实战指南

JavaScript 性能优化实战指南 前言 随着前端应用复杂度提升&#xff0c;JavaScript 性能瓶颈日益突出。高效的性能优化不仅能提升用户体验&#xff0c;还能增强系统稳定性和可维护性。本文系统梳理了 JavaScript 性能优化的核心思路、常见场景和实战案例&#xff0c;结合代码…

服务器磁盘按阵列划分为哪几类

以下是服务器磁盘阵列&#xff08;RAID&#xff09;的详细分类及技术解析&#xff0c;基于现行行业标准与实践应用&#xff1a; 一、主流RAID级别分类 1. ‌RAID 0&#xff08;条带化&#xff09;‌ ‌技术原理‌&#xff1a;数据分块后并行写入多块磁盘&#xff0c;无…

鸿蒙 Location Kit(位置服务)

移动终端设备已经深入人们日常生活的方方面面&#xff0c;如查看所在城市的天气、新闻轶事、出行打车、旅行导航、运动记录。这些习以为常的活动&#xff0c;都离不开定位用户终端设备的位置。 Location Kit 使用多种定位技术提供服务&#xff0c;可以准确地确定设备在室外/室…

二叉树深搜:在算法森林中寻找路径

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、搜索算法 二、回溯算法 三、例题讲解 3.1. 计算布尔二叉树的值 3.2. 求根节点到叶节点数字之和 3.3. 二叉树剪枝 3.4. 验证二叉搜索树 3.5. 二叉搜索树中第 K 小的元素 3.6. 二叉树的所有路径 …

企业级AI搜索解决方案:阿里云AI搜索开放平台

随着信息技术的飞速发展&#xff0c;搜索引擎作为信息获取的重要工具&#xff0c;扮演着不可或缺的角色。阿里云 AI 搜索开放平台以其强大的技术支持和灵活的开放性&#xff0c;持续为用户提供高效的搜索解决方案。 一、阿里云 AI 搜索开放平台 一站式的 AI 搜索开放平台作为…

自动驾驶中的预测控制算法:用 Python 让无人车更智能

自动驾驶中的预测控制算法:用 Python 让无人车更智能 自动驾驶技术近年来取得了令人惊叹的进步,AI 与边缘计算的结合让车辆能够实时感知环境、规划路径并执行驾驶决策。其中,预测控制(Model Predictive Control,MPC) 作为一种先进的控制算法,凭借其对未来驾驶行为的优化…