302 任务安排3(斜率优化)

1. 问题描述:

有 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
来源:https://www.acwing.com/problem/content/304/

2. 思路分析:

这道题目其实是301题的扩展,这里每一个点的斜率并一定是递增的,所以我们在查找的时候不能够直接将队列中的点删掉,这里我们可以使用二分找出第一个大于当前斜率s + t[i]的点,找到这个点f[j]来更新当前的f[i],接下来的过程与301题的单调队列维护所有点的斜率都是单调递增的过程是一样的。

3. 代码如下:

if __name__ == "__main__":
    n, s = map(int, input().split())
    t, c = [0], [0]
    for i in range(1, n + 1):
        a, b = map(int, input().split())
        t.append(t[i - 1] + a)
        c.append(c[i - 1] + b)
    f = [0] * (n + 1)
    q = [0] * (n + 1)
    hh, tt = 0, 0
    # 而对于每一个f[i]斜率不是递增的则需要使用二分找到第一个大于当前斜率的f[j], 用找到的f[j]来更新当前的f[i], 那么得到的f[i]的花费就是最小的
    for i in range(1, n + 1):
        l, r = 0, tt
        # 二分的过程, 队列中找到第一个斜率大于当前直线斜率的点
        while l < r:
            mid = l + r >> 1
            if f[q[mid + 1]] - f[q[mid]] > (t[i] + s) * (c[q[mid + 1]] - c[q[mid]]): r = mid
            else: l = mid + 1
        j = q[r]
        f[i] = f[j] - (t[i] + s) * c[j] + t[i] * c[i] + s * c[n]
        # 单调队列过程, 单调队列维护的其实是所有斜率都是递增的f[j]
        while hh < tt and (f[q[tt]] - f[q[tt - 1]]) * (c[i] - c[q[tt - 1]]) >= (f[i] - f[q[tt - 1]]) * (c[q[tt]] - c[q[tt - 1]]): tt -= 1
        tt += 1
        q[tt] = i
    print(f[n])
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值