#3023 任务安排
题面
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。
输出
一个数,最小的总费用。
样例输入
5
1
1 3
3 2
4 3
2 3
1 4
样例输出
153
提示
1<=N<=10000,1<=S<=50,1<=Ti,ci<=100
SOL
斜率优化的模板题之一。
最开始很容易想到一个二维的状态定义表示前个任务被分成批完成的最小费用,但是这样显然是的。
于是想到去掉完成批数的那一维——因为你不需要知道当前做到了第几批,你只需要知道你再分出一批来对后继状态的影响。
于是有:
注意:均为前缀和,不是题目描述的那种。
你将分为新的一批,会使后面的所有任务总共增加的费用。
于是算法被优化到了,可以过的点。
还可不可以再优化呢?看到min我们容易去想用一个单调队列去维护,但是直接维护这个min显然不可行。
这时候我们如果抛开min,将整个式子看成一个函数,于是你可以发现两个变量——。使前者作为因变量,后者为自变量,则可以得到如下函数:
这很像,有斜率,有截距,而且截距最小的时候,最小。
于是考虑怎样使截距最小。
如果把构成的点集表示在函数图像上,应该是这样的:
看上去很乱,但是如果你把相邻的三个点单独拿出来看:
于是可以知道,当时,才是有效决策。
(表示连线的斜率)
所以我们应该维护一个相邻两点连线斜率的单调递增队列,即一个下凸壳。
我们只需要保留斜率大于k、单调递增的部分即可。
这样做的效率是的。
代码:
#include<bits/stdc++.h>
#define N 10005
#define int long long
using namespace std;
inline int rd(){
int data=0,w=1;static char ch=0;ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
return data*w;
}
int n,s,f[N],t[N],c[N],q[N];
signed main(){
n=rd();s=rd();
for(int register i=1;i<=n;i++){
int register ti=rd(),ci=rd();
t[i]=t[i-1]+ti;c[i]=c[i-1]+ci;
}
memset(f,0x5f,sizeof(f));f[0]=0;
int l=1,r=1;q[l]=0;
for(int register 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++;
//维护斜率大于k
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]])>=(f[i]-f[q[r]])*(c[q[r]]-c[q[r-1]]))r--;
//维护下凹性质
q[++r]=i;
}
printf("%lld",f[n]);
return 0;
}
Update:
如果ti可以为负数呢?
s+t[i]不能单调递增,因此此时维护的不只是大于k的部分,需要维护整个下凸壳,因此需要二分一下找到那个左边斜率小于k且右边斜率大于k的点。
二分代码:
inline int search(int i,int k){
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];
}
题面详见WOJ #3622