CSP-S选手学习斜率优化详解
斜率优化详解
1. 什么是斜率优化?
斜率优化是一种用于优化动态规划的高级技巧,主要用于解决形如:
dp[i] = min{ dp[j] + f(i, j) } (j < i)
的递推式,通过将问题转化为凸包维护来降低时间复杂度。
2. 适用场景
斜率优化适用于状态转移方程可以写成:
dp[i] = min{ dp[j] + a[i] × b[j] + c[i] + d[j] }
其中 a[i]
、c[i]
只与 i
有关,b[j]
、d[j]
只与 j
有关。
3. 基本原理
3.1 数学推导
将状态转移方程改写为:
dp[i] - c[i] = min{ dp[j] + d[j] + a[i] × b[j] }
令:
y = dp[j] + d[j]
x = b[j]
k = a[i]
则问题转化为:对于每个 i
,找到 j
使得直线 y = kx + b
的截距最小。
3.2 凸包维护
- 将每个状态
j
看作平面上的点(x, y)
- 维护这些点的下凸包
- 用斜率为
k
的直线去切凸包,切点就是最优决策
4. 经典例题及代码实现
例题1:任务安排问题(最基础的斜率优化)
问题描述:有n个任务,每个任务有完成时间T[i]和费用系数C[i],将任务分批,每批任务有一个启动时间S,求最小总费用。
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
using namespace std;typedef long long ll;
const int N = 300010;ll n, s;
ll T[N], C[N];
ll dp[N];
ll sumT[N], sumC[N];
int q[N]; // 单调队列int main() {cin >> n >> s;for (int i = 1; i <= n; i++) {cin >> T[i] >> C[i];sumT[i] = sumT[i - 1] + T[i];sumC[i] = sumC[i - 1] + C[i];}int hh = 0, tt = 0; // 队首队尾q[0] = 0; // 初始状态for (int i = 1; i <= n; i++) {// 斜率 k = s + sumT[i]ll k = s + sumT[i];// 队首出队:如果队首两个点形成的斜率小于当前k,则队首不是最优while (hh < tt && (dp[q[hh + 1]] - dp[q[hh]]) <= k * (sumC[q[hh + 1]] - sumC[q[hh]])) {hh++;}int j = q[hh];dp[i] = dp[j] + s * (sumC[n] - sumC[j]) + sumT[i] * (sumC[i] - sumC[j]);// 队尾维护凸包while (hh < tt && (dp[q[tt]] - dp[q[tt - 1]]) * (sumC