首页 > 其他分享 >斜率优化DP

斜率优化DP

时间:2025-01-17 15:43:24浏览次数:1  
标签:int mid 凸包 斜率 DP 凸壳 优化 dp

斜率优化DP

例题

HNOI2008 玩具装箱

朴素dp

设 \(dp_i\) 表示前 \(i\) 个物品,分若干段的最小代价。

状态转移方程为:

\[dp_{i}=\min _{j<i}\left\{dp_{j}+\left(i-(j+1)+s_{i}-s_{j}-L\right)^{2}\right\}=\min _{j<i}\left\{dp_{j}+\left(s_{i}-s_{j}+i-j-1-L\right)^{2}\right\} \]

其中 \(pre_i\) 表示前 \(i\) 个数的和,即 \(\sum_{j=1}^i c_j\)。

该做法时间复杂度为 \(O(n^2)\),直接爆炸。

优化

为方便表示,令 \(L'=L+1\)

我们将式子改写为如下形式:

\[\begin{align*} dp_i &= dp_j+(s_i-s_j+i-j-L')^2 \\ dp_i &= dp_j+((s_i+i-L')+(-s_j-j))^2 \\ dp_i &= dp_j+(s_i+i-L')^2+2(s_i+i-L')(-s_j-j)+(-s_j-j)^2 \\ dp_i-(s_i+i-L')^2 &= dp_j+(s_j+j)^2-2(s_i+i-L')(s_j+j) \end{align*} \]

考虑一次函数式子 \(y=kx+b\),将其移项得到 \(b=y-kx\)。我们将只与 \(j\) 有关的信息表示为 \(y\),把同时与 \(i,j\) 有关的信息表示为 \(kx\)(其中与 \(i\) 相关的作 \(k\),与 \(j\) 相关的作 \(x\)),把只与 \(i\) 有关的信息(要求的信息)表示为 \(b\)。具体地,设

\[\begin{align*} b_i &= dp_i-(s_i+i-L')^2 \\ k_i &= 2(s_i+i-L') \\ y_j &= dp_j+(s_j+j)^2 \\ x_j &= s_j+j \end{align*} \]

则转移方程 \(dp_i-(s_i+i-L')^2 = dp_j+(s_j+j)^2-2(s_i+i-L')(s_j+j)\) 就可以写作

\[b_i=\min_{j<i}\{y_j-k_ix_j\} \]

我们把 \((x_j,y_j)\) 看作二维平面上的点,则 \(k_i\) 表示直线斜率, \(b_i\) 表示一条经过 \((x_j,y_j)\) 的斜率[1]为 \(k_i\) 的直线的截距。问题转化为了:选择合适的 \(j \ (1 \le j < i)\),最小化直线的截距(即 \(b_i\))

如图,我们将这个斜率为 \(k_i\) 的直线从下往上平移,直到有一个点 \((x_p,y_p)\) 在这条直线上,则有 \(b_i=y_p-k_ix_p\),这时 \(b_i\) 取到最小值。算完 \(f_i\),我们就把这个点 \((x_i,y_i)\) 这个点加入点集中,以做为新的 dp 决策。那么,我们该如何维护点集?

容易发现,可能让 \(b_i\) 取到最小值的点一定在凸壳上。因此在寻找 \(p\) 的时候我们就不需要枚举所有 \(i-1\) 个点,只需要考虑凸包上的点。在本题中 \(k_i\) 随着 \(i\) 的增加而增加,因此我们可以单调队列维护凸包

具体地,设 \(K(a,b)\) 表示过 \((x_a,y_a)\) 和 \((x_b,y_b)\) 的直线的斜率。考虑单调队列 \(q_l,q_{l+1},\dots,q_r\),维护的时下凸包上的点。也就是说,对于 \(l<i<r\),始终有 \(K(q_{i-1},q_i)<K(q_i,q_{i+1})\) 成立。

我们维护一个指针 \(e\) 来计算 \(b_i\) 的最小值。我们需要找到一个 \(K(q_{e-1},q_e)\le k_i <K(q_e,q_{e+1})\) 的 \(e\),这时就有 \(p=q_e\),即 \(q_e\) 是 \(i\) 的最优决策点。

在插入一个点 \((x_i,y_i)\) 时,我们要判断是否 \(K(q_{r-1},q_r)<K(q_r,i)\),如果不等式不成立就将 \(q_r\) 弹出,直到等式满足。然后将 \(i\) 插到 \(q\) 队尾。

代码实现

  1. 初始状态入队
  2. 每次使用一条和 \(i\) 相关的直线 \(f(i)\) 去切维护的凸包,找到最优决策,更新 \(dp_i\)。
  3. 加入状态 \(dp_i\)。如果一个状态(即凸包上的一个点)在 \(dp_i\) 加入后不再是凸包上的点,需要在 \(dp_i\) 加入前将其剔除。

时间复杂度 \(O(n)\)。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 5e4 + 10;
int n, L;
int c[N], s[N];
int dp[N], q[N], head = 1, tail = 0;
int b(int i) {return dp[i] - (s[i] + i - L - 1) * (s[i] + i - L - 1); } // 定义 b
int k(int i) {return 2 * (s[i] + i - L - 1); }							// 定义 k
int y(int j) {return dp[j] + (s[j] + j) * (s[j] + j); }					// 定义 y
int x(int j) {return s[j] + j; }										// 定义 x
double slope(int i, int j) {											// 定义 K(a, b)
	return 1.0 * (y(i) - y(j)) / (x(i) - x(j));
}
signed main() {
	scanf("%lld%lld", &n, &L);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &c[i]);
		s[i] = s[i - 1] + c[i];
	}
	memset(dp, INF, sizeof(dp));
	dp[0] = 0;
	q[++tail] = 0;
	for (int i = 1; i <= n; i++) {
		while (head < tail && slope(q[head], q[head + 1]) < k(i)) { // 改点不可能是最优决策点,出队
			head++;
		}
		dp[i] = y(q[head]) - k(i) * x(q[head]) + (s[i] + i - L - 1) * (s[i] + i - L - 1);
		while (head < tail && slope(q[tail - 1], q[tail]) > slope(q[tail], i)) { // 若该点不再是凸壳上的点,则将其踢出队列
			tail--;
		}
		q[++tail] = i;
	}
	printf("%lld\n", dp[n]);
	
	return 0;
}

小结

具体的代码实现因题就题,比较抽象,要考虑是上凸包还是下凸包,事踢对头还是踢队尾之类的。注意上凸包斜率越来越小,下凸包斜率越来越大

CDQ优化

一般用不上,但是放在这里。

对于有些问题,斜率并不是单调的。这时我们需要维护凸包上的每一个节点,然后每次用当前的直线去切这个凸包。

本题与「玩具装箱」问题唯一的区别是,玩具的价值可以为负。延续之前的思路,设 \(dp_i\) 表示前 \(i\) 个物品,分若干段的最小代价。

状态转移方程为:

\[dp_{i}=\min _{j<i}\left\{dp_{j}+\left(s_{i}-s_{j}+i-j-1-L\right)^{2}\right\} \]

其中 \(pre_i\) 表示前 \(i\) 个数的和,即 \(\sum_{j=1}^i c_j\)。

将方程做相同的变换:

\[dp_i-(s_i+i-L')^2 = \min_{j<i}\{dp_j+(s_j+j)^2-2(s_i+i-L')(s_j+j)\} \]

然而这时有两个条件不成立了:

  1. 直线的斜率不再单调;
  2. 每次加入的决策点的横坐标不再单调。

仍然考虑凸壳的维护。

在寻找最优决策点,也就是用直线切凸壳的时候,我们将单调队列找队首改为:凸壳上二分。我们二分出斜率最接近直线斜率的那条凸壳边,就可以找到最优决策。

在加入决策点,也就是凸壳上加一个点的时候,我们有两种方法维护。

第一种方法是直接用平衡树维护凸壳。那么寻找决策点的二分操作就转化为在平衡树上二分,插入决策点就转化为在平衡树上插入一个结点,并删除若干个被踢出凸壳的点。此方法思路简洁但实现繁琐。

来说一种基于 CDQ 分治的做法。

设 \(CDQ(l, r)\) 表示计算 \(f_i,i\in[l,r]\)。考虑 \(CDQ(1,N)\):

  • 我们先调用 \(CDQ(1,mid)\) 算出 \(f_i,i\in[1,mid]\)。然后我们对 \([1,mid]\) 这个区间内的决策点建凸包,然后用这个凸包去更新 \(f_i,i\in[mid+1,n]\)。这时我们决策点集是固定的,不像之前那样边计算 DP 值边加入决策点,那么我们就可以把 \(i\in[mid+1,n]\) 的 \(f_i\) 先按照直线的斜率 \(k_i\) 排序,然后就可以使用单调队列来计算 DP 值了。
  • 对于 \([mid+1,n]\) 中的每个点,如果它的最优决策的位置是在 \([1,mid]\) 这个区间,在这一步操作中他就会被更新成最优答案。当执行完这一步操作时,我们发现 \([1,mid]\) 中的所有点已经发挥了全部的作用,凸壳中他们存不存在已经不影响之后的答案更新。因此我们可以直接舍弃这个区间的决策点,并使用 \(CDQ(mid+1,n)\) 解决右区间剩下的问题。

时间复杂度 \(O(n\log^2n)\)。

小结

对比「玩具装箱」和「玩具装箱 改」,可以总结出以下两点:

  • 二分/CDQ/平衡树等能够优化 DP 方程的计算,于一定程度上降低复杂度,但不能改变这个方程本身。
  • DP 方程的性质会取决于数据的特征,但 DP 方程本身取决于题目中的数学模型。

  1. 一个点 \((x,y)\) 的斜率为 \(\frac{y}{x}\)。 ↩︎

标签:int,mid,凸包,斜率,DP,凸壳,优化,dp
From: https://www.cnblogs.com/zsk123qwq/p/18374226

相关文章

  • 三大主流国际课程IBDP、AP、A-Level的区别是什么?-季遇教育
      最近咨询季遇老师的有不少孩子就读双轨制的学校的家长,还有很多想要体制内转轨的家长,都会问到如何选择合适的国际课程。今天就给大家介绍一下三大主流课程的区别!  IB课程  IB一共分为四个学段:IBPYP(小学)、IBMYP(初中)、IBDP(高中)、IBCP(职业教育)四个学段,其中IBDP......
  • CSP2025 - 树形 DP
    CSP2025-树形DPT1「MXOIRound1」城市这个“树上两点距离之和”很典,让我们想到换根DP。首先求出\(\text{siz}_u\)和\(d_u\),分别表示子树\(u\)的大小和子树所有点到\(u\)的距离之和。接下来求出整棵树的所有点到\(\boldsymbolu\)的距离之和。考虑用\(d_u\)......
  • ThreadPool解析
    Thread_Pool项目解析简介ThreadPool是一个轻量级的C++线程池实现,旨在简化多线程编程。项目分析我们首先上github的项目地址:https://github.com/progschj/ThreadPool,然后克隆项目到本地。点开项目的ThrealPool.h文件,查看源码:#ifndefTHREAD_POOL_H#defineTHREAD_POOL......
  • MySql操作指南4--MySQL查询优化与性能调优
    性能优化是数据库开发的重要组成部分,合理优化SQL查询、索引设计以及程序逻辑能够大幅提升系统性能。本文将详细介绍SQL查询优化、Golang中的性能调优技术以及MySQL的索引与分区管理方法。1、SQL查询优化 查询计划的作用查询计划是数据库执行SQL语句的过程及其成......
  • 【金融资产组合模型进化论】4.1 对MPT+Fama-French五因子优化方案实现Backtrader量化
    目录0.承前1.汇总代码2.近4年量化回测2.1获取近4年资产组合数据2.2对近4年资产组合数据进行量化回测3.启后3.1待优化点0.承前本篇博文是对文章,链接:【金融资产组合模型进化论】4.马科维茨资产组合模型+Fama-French五因子优化方案(理论+Python实战)实现量......
  • Java百万数据导出Excel性能优化[读(并发)写分离/流式查询]
    参考:https://www.zhihu.com/tardis/bd/art/533753443?source_id=1001 Java百万数据导出Excel性能优化[读(并发)写分离/流式查询]结果测试:104万数据,导出excel用时由59秒优化到19秒问题列表:1、导出过程中会较多占用CPU、内存、磁盘,需全局对Excel导出限流,防止同时对大量数......
  • vue性能优化
    异步组件<template><Suspensev-if="!routerLoading"><template#default><AsyncComp/></template><template#fallback><divclass="loading-container"><divcla......
  • 智能关键技术三:智能优化器
    贝叶斯网络模型原理贝叶斯网络是一种概率图模型,拓扑结构通常为一个有向无环图。贝叶斯网络的优势在于能够利用条件独立假设对多变量数据进行建模,并且自适应变量之间的相关性,具体是指每个变量的概率分布只和与它直接连接的父亲节点有关。使用这种方法能够比基于简单的独立性假设的......
  • GaussDB云原生数据库SQL引擎继承原来openGauss的词法解析,语法解析,查询重写,查询优化和
    云原生数据库SQL引擎继承原来openGauss的词法解析,语法解析,查询重写,查询优化和执行引擎的能力。由于云原生数据库是shareddisk架构,一个事务在一个节点上执行,所以不需要原来分布式根据分布式key进行数据分布,分布式执行和分布式2PC提交的能力。为了支持数据库粒度的异地多活,云原生......
  • G1原理—9.如何优化G1中的MGC
    大纲1.大对象导致频繁MixedGC的案例2.MixedGC到底是在优化什么(从避免到提速)3.MixedGC相关参数详解之堆内存分配参数4.MixedGC其他相关的参数详解及优化 1.大对象导致频繁MixedGC的案例(1)案例背景(2)问题现场(3)Redis缓存有什么问题(4)缓存同步服务有什么问题(......