斜率优化DP(二):任务安排3

本文介绍了一种解决大规模任务调度问题的算法,针对NNN个任务的执行顺序不可改变,机器需按批次处理并考虑启动时间和任务费用系数。通过调整队列操作策略,结合二分搜索,有效地降低了查询时间复杂度,目标是找到使总费用最低的任务分组方案。
摘要由CSDN通过智能技术生成

题目描述

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

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

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

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

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

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

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

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

输入格式

第一行包含整数 N N N

第二行包含整数 S S S

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

输出格式

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

数据范围

1 ≤ N ≤ 3 × 1 0 5 1≤N≤3\times10^5 1N3×105 ,
1 ≤ T i , C i ≤ 512 1≤T_i,C_i≤512 1Ti,Ci512,
− 512 ≤ S ≤ 512 -512≤S≤512 512S512

输入样例

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

输出样例

153

算法思想

任务安排2中斜率 k ( S u m T i + S k (Sum_{T_i}+S k(SumTi+S)是单调递增的,向后计算时新加入点的 x ( S u m C j ) x(Sum_{C_j}) x(SumCj)坐标也是单调递增的,那么可以使用队列存储所有计算过的点:

  • 在查询时,可以将队头小于等于当前斜率的点全部出队,即 f h h + 1 − f h h S u m C h h + 1 − S u m C h h ≤ S u m T i + S \frac{f_{hh+1}-f_{hh}}{Sum_{C_{hh+1}}-Sum_{C_{hh}}}\le Sum_{T_i}+S SumChh+1SumChhfhh+1fhhSumTi+S。因为斜率单调递增,后面只会越来越大。
  • 在插入时,可以将队尾大于等于当前斜率的点(不在凸包上的点)出队,即 f t t − f t t − 1 S u m C t t − S u m C t t − 1 ≥ f i − f t t S u m C i − S u m C t t \frac{f_{tt}-f_{tt-1}}{Sum_{C_{tt}}-Sum_{C_{tt-1}}}\ge \frac{f_i-f_{tt}}{Sum_{C_i}-Sum_{C_{tt}}} SumCttSumCtt1fttftt1SumCiSumCttfiftt。因为不在凸包上,意味着不可能有最小值。

对比任务安排2,本题中 S S S的数据范围发生了变化( − 512 ≤ S ≤ 512 -512≤S≤512 512S512),那么斜率 k ( S u m T i + S k (Sum_{T_i}+S k(SumTi+S)不满足单调性质,因此在查询时不能将队头出队。为了提高查询效率,可以使用二分搜索来确定第一个大于当前斜率的点在队列中的位置。因此:

  • 在查询时,使用二分搜索,找到第一个 f m i d + 1 − f m i d S u m C m i d + 1 − S u m C m i d > S u m T i + S \frac{f_{mid+1}-f_{mid}}{Sum_{C_{mid+1}}-Sum_{C_{mid}}}\gt Sum_{T_i}+S SumCmid+1SumCmidfmid+1fmid>SumTi+S m i d mid mid表示符合条件的点在队列中的位置。
  • 在插入时,可以将队尾大于等于当前斜率的点(不在凸包上的点)出队,即 f t t − f t t − 1 S u m C t t − S u m C t t − 1 ≥ f i − f t t − 1 S u m C i − S u m C t t − 1 \frac{f_{tt}-f_{tt-1}}{Sum_{C_{tt}}-Sum_{C_{tt-1}}}\ge \frac{f_i-f_{tt-1}}{Sum_{C_i}-Sum_{C_{tt-1}}} SumCttSumCtt1fttftt1SumCiSumCtt1fiftt1。因为不在凸包上,意味着不可能有最小值。

注意:这里之所以使用二分搜索来查找,是因为队列中只维护了凸包边界上的点,而凸包的斜率一定是单调。

时间复杂度

在每个任务中都需要进行二分搜索,所以时间复杂度为 O ( n l o g ) O(nlog) O(nlog)

代码实现

#include <iostream>

using namespace std;
typedef long long LL;
const int N = 300010;

LL t[N], c[N], f[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;
    q[tt] = 0; // 将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]])) r = mid;
            else l = mid + 1;
        }
        
        int j = q[r];
         //计算f[i]
        f[i] = f[j] + t[i] * (c[i] - c[j]) + s * (c[n] - c[j]);
        //队列中至少保留两个点,并且将队尾不在凸包上的点出队
        //为了避免long long 溢出,这里强转为double进行大小比较
        while(hh < tt && (double)(f[q[tt]] - f[q[tt - 1]]) * (c[i] - c[q[tt - 1]]) >= (double)(f[i] - f[q[tt - 1]]) * (c[q[tt]] - c[q[tt - 1]])) tt --;
        q[++ tt] = i;
    }
    
    printf("%lld\n", f[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值