AcWing 302 任务安排3

题目描述:

有 N 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。

机器会把这 N 个任务分成若干批,每一批包含连续的若干个任务。

从时刻0开始,任务被分批加工,执行第 i 个任务所需的时间是 Ti。

另外,在每批任务开始前,机器需要 S 的启动时间,故执行一批任务所需的时间是启动时间 S 加上每个任务所需时间之和。

一个任务执行后,将在机器中稍作等待,直至该批任务全部执行完毕。

也就是说,同一批任务将在同一时刻完成。

每个任务的费用是它的完成时刻乘以一个费用系数 Ci。

请为机器规划一个分组方案,使得总费用最小。

输入格式

第一行包含两个整数 N 和 S。

接下来N行每行有一对整数,分别为 Ti 和 Ci,表示第 i 个任务单独完成所需的时间 Ti 及其费用系数 Ci。

输出格式

输出一个整数,表示最小总费用。

数据范围

1≤N≤3∗10^5
0≤S,Ci≤512
−512≤Ti≤512

输入样例:

5 1
1 3
3 2
4 3
2 3
1 4

输出样例:

153

分析:

AcWing 301 任务安排2中,我们给出了任务安排问题的斜率优化的解法,因为斜率k = st[i] + S是单调递增的,所以队列中小于当前k的斜率一定小于后面的k,于是我们在查找第一个大于k的斜率时将小于k的斜率都删除了,从而保证了O(n)的时间复杂度。但是本题的Ti可能是负数,尽管时间是负数设置得有些不合理,我们只能暂且认为它是合理的,Ti是负数时,k = st[i] + S就未必单调递增了,因为前缀和不一定单调递增了,这意味着,后面的k可能小于之前的k,我们不应该在寻找第一个大于k的斜率时删掉小于k的斜率了,因为后面的k还可能用到,但是不删除小于k的斜率,意味着每次寻找j都需要从堵头找起,时间复杂度最坏可能是平方级别的,为此我们在找第一个大于k的斜率时,只能对队列中的斜率做二分查找,用O(nlogn)的时间复杂度去解决本题了。本题的其它代码与上题一致,故分析过程可以参考上题,唯一修改的地方就是将原来出队头的代码修改为二分找j的代码了。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 300005;
typedef long long ll;
ll f[N],t[N],c[N];
int q[N];
int main(){
    int n,s;
    scanf("%d%d",&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];
    }
    int hh = 0,tt = 0;
    for(int i = 1;i <= n;i++){
        int l = hh,r = tt;
        while(l < r){
            int mid = l + r >> 1;
            if(f[q[mid+1]]-f[q[mid]]<=(t[i]+s)*(c[q[mid+1]]-c[q[mid]]))     l = mid + 1;
            else    r = mid;
        }
        f[i] = f[q[l]] - (t[i] + s) * c[q[l]] + c[i] * t[i] + s * c[n];
        while(hh < tt && (f[q[tt]]-f[q[tt - 1]])*(c[i]-c[q[tt]]) >= (f[i]-f[q[tt]])*(c[q[tt]]-c[q[tt - 1]]))    tt--;
        q[++tt] = i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值