斜率优化DP
总述
维护队列中相邻两个元素的某种“比值”的“单调性”
因为该比值对应坐标系中的斜率
所以称为斜率优化
英文称为\(convex\space hull\space trick\)(直译:凸壳优化策略)
[例1]「TYVJ1098」任务安排 1
N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。 例如:S=1;T={1,3,4,2,1};F={3,2,3,3,4}。如果分组方案是{1,2}、{3}、{4,5},则完成时间分别为{5,5,10,14,14},费用C={15,10,30,42,56},总费用就是153。
输入
第一行是N(1< =N< =5000)。 第二行是S(0< =S< =50)。 下面N行每行有一对数,分别为Ti和Fi,均为不大于100的正整数,表示第i个任务单独完成所需的时间是Ti及其费用系数Fi。
输出
一个数,最小的总费用。
样例
样例输入1
5
1
1 3
3 2
4 3
2 3
1 4
样例输出1
153
solution
状态:\(f[i][j]\),前 \(i\) 个任务分成 \(j\) 批执行的最小费用
决策:考虑第 \(j\) 批执行包含的任务
状态转移方程:
-
\(𝑠𝑢𝑚𝑇[𝑖] =\sum^i_{𝑗=1}𝑇[𝑗]\)
-
\(𝑠𝑢𝑚C[𝑖] =\sum^i_{𝑗=1}C[𝑗]\)
-
\(𝑓[𝑖][𝑗] =𝑚𝑖𝑛_{0≤k≤i}(𝑓[𝑘][𝑗 − 1] + (𝑆 ∗ 𝑗 + 𝑠𝑢𝑚𝑇[𝑖]) ∗ (𝑠𝑢𝑚𝐶[𝑖] − 𝑠𝑢𝑚𝐶[𝑘]))\)
考虑第 \(j\) 批执行的是 \((k+1)\)~\(i\) 个任务
枚举第 \(j-1\) 批和第 \(j\) 批的分界点 \(k\) 为DP的决策
时间复杂度为\(O(N^3)\)。
状态能否优化?
题目并没有规定分成多少批次。
之所以需要批次,是因为想知道有多少次启动时间\(S\),从而计算出每批任务完成的时间
实际上,可以将每批任务花费的启动时间\(S\),对之后任务的影响提前计算
状态:
- \(f[i]\),表示前 \(i\) 个任务划分成若干批执行的最小费用。
考虑当前批次执行的任务,状态转移方程:
- \(𝑓[𝑖] =𝑚𝑖𝑛_{0≤j<i} ( 𝑓[j] +sumT[i]∗(sumC[i]-sumC[j])+𝑆 ∗ (𝑠𝑢𝑚𝐶[N] − 𝑠𝑢𝑚𝐶[j]))\)
当前批次执行的任务为第 \(j+1\)~\(i\) 个任务
机器的启动时间会对第 \(j\) 个任务以后的所有任务产生影响,提前将影响累加到最小费用中
这就是“费用提前计算”的经典思想
时间复杂度为\(O(N^2)\)
AC code
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+2;
int n,s;
int t[N],c[N],st[N],sc[N],f[N];
int main(){
scanf("%d",&n);
scanf("%d",&s);
for(int i=1;i<=n;i++){
scanf("%d%d",&t[i],&c[i]);
st[i]=st[i-1]+t[i];
sc[i]=sc[i-1]+c[i];
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
f[i]=min(f[i],f[j]+st[i]*(sc[i]-sc[j])+s*(sc[n]-sc[j]));
}
}
printf("%d",f[n]);
return 0;
}
[例2]「POJ1180/IOI2002」任务安排 2
N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
例如:S=1;T={1,3,4,2,1};F={3,2,3,3,4}。如果分组方案是{1,2}、{3}、{4,5},则完成时间分别为{5,5,10,14,14},费用C={15,10,30,42,56},总费用就是153。
输入
第一行是N(1<=N<=300000)。
第二行是S(0<=S<=512)。
下面N行每行有一对数,分别为Ti和Fi,均为不大于100的正整数,表示第i个任务单独完成所需的时间是Ti及其费用系数Fi。
输出
一个数,最小的总费用。
样例
样例输入1
5
1
1 3
3 2
4 3
2 3
1 4
样例输出1
153
solution
\(𝑓[𝑖] =𝑚𝑖𝑛_{0≤j<i} ( 𝑓[j] +sumT[i]∗(sumC[i]-sumC[j])+𝑆 ∗ (𝑠𝑢𝑚𝐶[N] − 𝑠𝑢𝑚𝐶[j]))\)
- 同时和变量 \(i\) 和变量 \(j\) 有关,单调队列不适用
将\(min\)函数去掉,把关于 \(j\) 的值 \(f[j]\) 和 \(sumC[j]\) 看作变量,其余部分看作常数。
移项得到:
- \(𝑓[𝑗] = (𝑠𝑢𝑚𝑇[𝑖] + 𝑆) ∗ 𝑠𝑢𝑚𝐶[𝑗] + 𝑓[𝑖] − 𝑠𝑢𝑚𝑇[𝑖] ∗ 𝑠𝑢𝑚𝐶[𝑖] − 𝑆 ∗ 𝑠𝑢𝑚𝐶[𝑁]\)
将 \(sumC[j]\) 看作是横坐标,\(f[j]\)看作是纵坐标,就是一个形如 \(y=ax+b\)的一条直线。
斜率:\(𝑠𝑢𝑚𝑇[𝑖] + S\)
截距:\(𝑓[𝑖] − 𝑠𝑢𝑚𝑇[𝑖] ∗ 𝑠𝑢𝑚𝐶[𝑖] − 𝑆 ∗ 𝑠𝑢𝑚𝐶[𝑁]\)
对于每一个决策 j,都对应直角坐标系中的一个点 (sumC[j],f[j])
当变量 \(i\) 不变时,直线的斜率是固定值\(𝑠𝑢𝑚𝑇[𝑖] + 𝑆\)。
\(sumT[i]\),\(sumC[i]\),\(S\),\(sumC[N]\)为固定值。当截距取得最小值时,f[i]也取到最小值。
求解最小截距的方法:
-
将斜率为\(𝑠𝑢𝑚𝑇[𝑖] + 𝑆\) 的直线经过每一个决策点,截距最小的为最优决策。
-
将直线从下往上移动,遇到的第一个决策点就是最优决策。
所有的决策点\((sum[j],f[j])\)都是有用的吗?
利用“及时排除无用决策”思想,将无用的决策点删除。
假设存在三个决策 \(j1\),\(j2\),\(j3\),对应的决策点为\((sumC[j1],f[j1])\),\((sumC[j2],f[j2])\),\((sumC[j3],f[j3])\),设三点分别为\(A\),\(B\),\(C\)
不妨设 \(j1<j2<j3\),因为T,C均为整数,有\(sumC[j_1]<sumC[j_2]<sumC[j_3]\)
有两种情况:
-
对于上凸,无论斜率是多少,j2都不可能是最优决策,可以排除。
-
对于下凸,j2可能是最优决策,当且仅当:
\(\frac{f[j_2]-f[j_1]}{sumC[j_2]-sumC[j_1]}<\frac{f[j_3]-f[j_2]}{sumC[j_3]-sumC[j_2]}\)
将不可能的最优决策排除后,将剩下的点集相邻两点连线
形成的线段的斜率从左到右单调递增
需要维下凸壳
使用单调队列维护
对于斜率为k的直线,若某个点左侧线段的斜率小于k,右侧线段的斜率大于k,那么该点就是最优决策点
因为\(sumC\)是单调递增的,新的决策点的横坐标一定大于之前所有决策点的横坐标
斜率\(S+sumT[i]\)也是单调递增的
只维护相邻两点线段斜率大于\(S+sumT[i]\)的决策点,那么最优决策点就是队头
时间复杂度为\(O(N)\)
AC code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=300005;
ll n,s,t[N],c[N];
ll st[N],sc[N],dp[N], hd=1,tl=0,q[N];
double xl(ll i,ll j) {
return ((double)dp[j]-dp[i])/((double)sc[j]-sc[i]);
}
int main() {
scanf("%lld",&n);
scanf("%lld",&s);
for(ll i=1;i<=n;i++){
scanf("%lld%lld",&t[i],&c[i]);
st[i]=st[i-1]+t[i];
sc[i]=sc[i-1]+c[i];
}
q[++tl]=0;
dp[0]=0;
for(ll i=1; i<=n; i++) {
while(hd<tl&&xl(q[hd],q[hd+1])<=s+st[i]) hd++;
dp[i]=dp[q[hd]]-(s+st[i])*sc[q[hd]]+st[i]*sc[i]+s*sc[n];
while(hd<tl&&xl(q[tl-1],q[tl])>xl(q[tl],i)) tl--;
q[++tl]=i;
}
printf("%lld",dp[n]);
return 0;
}
完结撒花❀
★,°:.☆( ̄▽ ̄)/$:.°★ 。

浙公网安备 33010602011771号