[bzoj2726][DP]任务安排

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rose_max/article/details/81587145

Description

机器上有N个需要处理的任务,它们构成了一个序列。这些任务被标号为1到N,因此序列的排列为1,2,3…N。这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和。注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数Ci。请确定一个分组方案,使得总费用最小。

Input

第一行两个整数,N,S。 接下来N行每行两个整数,Ti,Ci。

Output

一个整数,为所求的答案。

Sample Input

5 1

1 3

3 2

4 3

扫描二维码关注公众号,回复: 2994982 查看本文章

2 3

1 4

Sample Output

153

题解

设t[i]表示t的前缀和 c[i]表示f的前缀和
首先dp方程给出
f [ i ] = m i n ( f [ j ] + t [ i ] ( c [ i ] c [ j ] ) + S ( c [ n ] c [ j ] ) )
此处影响释放给了后面的任务
大力斜率优化,可以发现在 j < k

f [ j ] f [ k ] c [ j ] c [ k ] < t [ i ] + S

k比j优秀
于是维护一个下凸壳
观察到t[i]没有单调性,不能直接单调队列
我们二分出第一个左边线段斜率小于 S + t [ i ] ,右边线段斜率大于 S + t [ i ] 的点
这个点就是最佳决策点

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL f[310000],c[310000],t[310000],S;
int n;
double slop(int j,int k){return (double)(f[j]-f[k])/(c[j]-c[k]);}
int li[310000],head,tail;
int fdans(LL gg)
{
    int l=head,r=tail;
    while(l<r)
    {
        int mid=(l+r)/2;
        if((f[li[mid]]-f[li[mid+1]])>gg*(c[li[mid]]-c[li[mid+1]]))l=mid+1;
    //    if(slop(li[mid],li[mid+1])<gg)l=mid+1;
        else r=mid;
    }
    return li[l];
}
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    scanf("%d%lld",&n,&S);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&t[i],&c[i]),t[i]+=t[i-1],c[i]+=c[i-1];
    f[0]=0;
    li[1]=0;head=tail=1;
    for(int i=1;i<=n;i++)
    {
        int p=fdans(t[i]+S);
        f[i]=f[p]+t[i]*(c[i]-c[p])+S*(c[n]-c[p]);
        while(head<tail && (f[li[tail-1]]-f[li[tail]])*(c[i]-c[li[tail]])<=(f[i]-f[li[tail]])*(c[li[tail-1]]-c[li[tail]]))tail--;
        li[++tail]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/81587145