DP优化之斜率优化小结
这或许是这几天的济南云斗集训之旅最大的收获吧,若是最后一天的模拟赛文件不会交错也许结局会更好,但在这残酷的现实中却从不会有“如果”一词,母亲以不想让我学了,或许考完今年的 CSP 就可能不学了吧。
本文将效仿《李煜东算法进阶指南》的思路,按照例题层层深入。
P2365 任务安排
题目链接
凡是先考虑朴素算法,这是一个好习惯。
朴素的解法
求出
朴素的解法复杂度
本题正解
思考为什么要
思考该如何优化,感觉不容易直接求之前此机器启动过几次,但机器每次启动所耗费的时间
设
下文摘自《李煜东算法进阶指南》:
也就是说我们没有直接求出每批的完成时刻,而是在一批任务“开始”对后续任务产生影响时,就先把费用累加到答案中。这是一种名为“费用前提计算”的经典思想。
很明显这段话可谓相当的晦涩。
该复杂度
咦,好尴尬,我似乎没有写过关于此解的的代码。
P10979 任务安排 2
题目链接
与上一题的题意一模一样,但数据加强了。
将上一题的转化方程拆一下,得:
由于
让我们把
在一个以
对于三个决策点
探究什么情况时,
如上图所示,
上示的不等式实际上是连接两个决策点连线的斜率,通俗的讲,我们应该维护“链接相邻两点的线段斜率” 单调递增的一个“下凸壳”
有这个下凸壳的顶点才有可能成为最优决策。实际上,对于一条斜率为
在本题中,
综上,对于代码操作,我们建立一个单调队列
对于每个决策
-
检查队头决策
q_l 和q_{l+1} ,若斜率(f_{q_{l+1}}-f_{q_l})/(c_{q_{l+1}}-c_{q_l})\le S+t_i ,则q_l 出队,检查新对头。 -
取队头
q_l 为最优决策,计算f_i 。 -
将新决策
i 队尾插入,插入前,若q_r 不满足下凸性,即q_r 是无用决策,就将q_r 出队,检查新队尾。
时间复杂度
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,s,c[N],t[N],f[N],q[N];
int main(){
cin>>n>>s;
for(int i=1;i<=n;i++){
cin>>t[i]>>c[i];
t[i]+=t[i-1],c[i]+=c[i-1];
}
int l=1,r=1;
memset(f,0x3f,sizeof(f));
f[0]=0;
q[1]=0;
for(int i=1;i<=n;i++){
int k = s+t[i] ;
while( l<r && f[q[l+1]]-f[q[l]] <= k * (c[q[l+1]]-c[q[l]]) ) ++l;
f[i]= f[q[l]] - k*c[q[l]] + t[i]*c[i] + s*c[n];
while( l<r && (f[q[r]]-f[q[r-1]]) * (c[i]-c[q[r]])>=(c[q[r]]-c[q[r-1]])*(f[i]-f[q[r]]))--r;
q[++r]=i;
}
cout<<f[n];
return 0;
}
以下摘自《李煜东算法进阶指南》:
与一般的单调队列优化 DP 的模型相比,本题维护的“单调性”依赖于队列中相邻的两个元素之间的某种“比值”。因为这个值对应线性规划的坐标系的斜率,所以我们把本题中使用的优化方法称为“斜率优化”,英文称为 convex hull trikk(直译为凸包优化策略)。
P5785 [SDOI2012] 任务安排
题目链接
与上一题不同的是这一题的
于是我们需要在单调队列中寻找一个点
对于这个搜索,我们可以二分搜索。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5;
int n,s,c[N],t[N],f[N],q[N],l,r,k;
int binary_search(int i){
if(l==r)return q[l];
int L=l,R=r;
while(L<R){
int mid=(L+R)>>1;
if(f[q[mid+1]]-f[q[mid]]<=k*(c[q[mid+1]]-c[q[mid]]))L=mid+1;
else R=mid;
}
return q[L];
}
signed main(){
cin>>n>>s;
for(int i=1;i<=n;i++){
cin>>t[i]>>c[i];
t[i]+=t[i-1],c[i]+=c[i-1];
}
l=r=1,q[1]=0;
for(int i=1;i<=n;i++){
k=s+t[i];
int p=binary_search(i);
f[i]=f[p]-k*c[p] + t[i]*c[i] + s*c[n];
while( l<r && (f[q[r]]-f[q[r-1]]) * (c[i]-c[q[r]])>=(c[q[r]]-c[q[r-1]])*(f[i]-f[q[r]]))r--;
q[++r]=i;
}
cout<<f[n];
return 0;
}
P10980 任务安排 4.1【暂无数据】
题目链接
若