P2365 任务安排
guoshaoyang · · 题解
P2365 任务安排
算法:dp+斜率优化
首先,动态规划的状态转移方程不难写出:
可写作:
这样的方程是
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1024*5,INF=1024*1024*1024;
int N,S,cost[MAXN],tim[MAXN]/**/,f[MAXN];
int main(){
scanf("%d%d",&N,&S);
for(int i=1;i<=N;i++){
scanf("%d%d",&tim[i],&cost[i]);
tim[i]+=tim[i-1];
cost[i]+=cost[i-1];
}
for(int i=1;i<=N;i++){
f[i]=INF;
for(int j=0;j<i;j++){
f[i]=min(f[i],f[j]+tim[i]*(cost[i]-cost[j])+S*(cost[N]-cost[j]));
}
}
printf("%d",f[N]);
return 0;
}
仔细观察方程,可以发现这道题可以斜率优化
设
∴\displaystyle\frac{f_j-f_k}{c_j-c_k}<t_i+S 即j 到k 的斜率小于t_i+S
∴以c_i 为横轴,f_i 为纵轴建立如图所示平面直角坐标系,维护一个下凸壳
step1:淘汰子问题维护队列时,将凸壳左端斜率太小的点删除(用f=t_i+S 切这个凸壳),最左端点根据上面的证明即为最优子问题
step2:用原方程转移,得到i 号点的横纵坐标
step3:插入i 号点,维护下凸壳(如图,在插入i 时,发现i 的斜率小于l ,即l 没前途,直接弹掉)
具体维护方法见代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1024*5,INF=1024*1024*1024;
int N,S,cost[MAXN],tim[MAXN],f[MAXN];
int dq[MAXN],L=1,R;
int main() {
scanf("%d%d",&N,&S);
for(int i=1; i<=N; i++) {
scanf("%d%d",&tim[i],&cost[i]);
tim[i]+=tim[i-1];
cost[i]+=cost[i-1];
}
dq[++R]=0;
for(int i=1; i<=N; i++) {
while(L<R&&(f[dq[L+1]]-f[dq[L]])<=(tim[i]+S)*(cost[dq[L+1]]-cost[dq[L]])) {
L++;
}
f[i]=f[dq[L]]-cost[dq[L]]*(tim[i]+S)+tim[i]*cost[i]+S*cost[N];
while(L<R&&(f[dq[R]]-f[dq[R-1]])*(cost[i]-cost[dq[R]])
>=(f[i]-f[dq[R]])*(cost[dq[R]]-cost[dq[R-1]])) {
R--;
}
dq[++R]=i;
}
printf("%d",f[N]);
return 0;
}