AcWing 凸包优化DP相关问题 302. 任务安排3

博客介绍了如何应用凸包优化解决类似任务安排的问题,特别是当直线斜率可正可负时,需要避免在处理斜率时出现错误。问题的核心在于处理可能的连续相同值,并确保在计算过程中避免除以0的情况。时间复杂度为O(NLogN)。
摘要由CSDN通过智能技术生成

问题根上一篇的任务安排2 差不多,唯一区别就是直线的斜率可正可负,因此上一个问题中,最后从队列头删除斜率小的元素的操作就不能有了,然后Sc序列里面可能出现连续的相同的值,注意下除以0的操作特殊处理下即可,时间复杂度O(NLogN),凸包优化思路是跟任务安排2 这个题目没差的,总的讲凸包优化这个思路还是比较难想到的

 

'''
dp 转移思想

dp(i)表示前i个任务所有分组方案中,总开销的最小值
按照最后一组的情况来划分集合,也就是倒数第二组的最后一个任务的编号,假设这个编号是j
注意有个巧妙的转化,在算每一组开始启动的时间S带来的开销时候,对这个组后面的所有任务
的带来的开销的增加量全部累加到当前这一组任务上,这样处理每一组任务时候,就可以排除
掉前面的组对当前组的开销的影响, dp(i)是已经包含了前i个元素所有分组对后面的所有分组
的开销带来的额外增量的

Sc 表示Ci的前缀和
St 表示Ti的前缀和

dp(i) = max{
    dp(i-1) + S*(Sc(N-1) - Sc(i-1)) + (Sc(i) - Sc(i-1)) * St(i),
    dp(i-2) + S*(Sc(N-1) - Sc(i-2)) + (Sc(i) - Sc(i-2)) * St(i),
    ....
    dp(i-i) + S*(Sc(N-1) - Sc(i-i)) + (Sc(i) - Sc(i-i)) * St(i),
    0 + S* (Sc(N-1) - 0) + (Sc(i) - 0) * St(i)
}


'''

from collections import deque

N, S = map(int, input().split())

St = [0] * N
Sc = [0] * N
for i in range(N):
    St[i], Sc[i] = map(int, input().split())
for i in range(1, N):
    St[i], Sc[i] = St[i] + St[i-1], Sc[i] + Sc[i-1]


# 凸包的下边界
que = deque()

# 因为Sc(-1) = 0, dp(-1) = 0, 一开始凸包里面就有这么一个初始点
que.append((-1, 0, -0x7fffffff)) # 三元组结构(下标i, dp(i), 和图标序列前一个点之间直线的斜率)

dp  = [0] * N
for i in range(N):
    cur_k = S + St[i]

    n = len(que)
    l, r = 0, n-1
    pos = -0x7fffffff
    while l <= r:
        mid = l + (r-l) // 2
        if que[mid][2] >= cur_k:
            pos = mid
            r = mid - 1
        else:
            l = mid + 1

    pos = n-1 if pos == -0x7fffffff else pos-1
    selected_k = que[pos][2]

    j = que[pos][0]
    if j == -1:
        dp[i] = St[i]*Sc[i] + S*Sc[N-1]
    else:
        dp[i] = dp[j] - cur_k * Sc[j] + (St[i]*Sc[i] + S*Sc[N-1])

    # 根据凸包下边界性质,比较斜率,更新凸包的下边界
    last_k = 0

    if len(que) > 1:
        while len(que) > 1:
            j = que[-2][0]
            Sc_val = 0 if j == -1 else Sc[j]

            if Sc[i] - Sc_val != 0:
                last_k = (dp[i] - que[-2][1]) / (Sc[i] - Sc_val)
            else:
                if dp[i] - que[-2][1] < 0:
                    last_k = -0x7fffffff
                elif dp[i] - que[-2][1] > 0:
                    last_k = 0x7fffffff
                else:
                    last_k = 0

            if que[-1][2] >= last_k:
                que.pop()
            else:
                break

    j = que[-1][0]
    Sc_val = 0 if j == -1 else Sc[j]

    if Sc[i] - Sc_val != 0:
        last_k = (dp[i] - que[-1][1]) / (Sc[i] - Sc_val)
    else:
        if dp[i] - que[-1][1] < 0:
            last_k = -0x7fffffff
        elif dp[i] - que[-1][1] > 0:
            last_k = 0x7fffffff
        else:
            last_k = 0
    que.append((i, dp[i], last_k))

print(dp[N-1])

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值