AcWing 凸包优化DP相关问题 303. 运输小猫

from collections import deque

N, M, P = map(int, input().split())
dis = [0, 0] + list(map(int, input().split()))

for i in range(1, len(dis)):
    dis[i] += dis[i-1]

A = []
for i in range(M):
    h, t = map(int, input().split())
    A.append(t - dis[h])
A.sort()
S = [val for val in A]
for i in range(1, M):
    S[i] += S[i-1]

'''
dp(i, j) 表示前j只猫i个饲养员送的最小开销,等价于把A前j个数分成最多i组,最小的总开销

dp(i, j) = min {
    (j-k)*A[j] - [S(j) - S(k)] + dp(i-1, k)
}

k = j-1, j-2, ......0, -1 (k = -1时候特殊处理,dp[i][k] = 0

两层循环,每一行迭代时候就是一轮一维的凸包优化DP


S[k] + dp(i-1, k) = A[j] * k + ( S(j) - j*A[j] + dp(i, j) )
看做关于k的直线,横轴是k, 纵轴是S[k] + dp(i-1, k) 

'''

dp = [[0] * (M+1) for _ in range(P+1)]

for i in range(1, P+1):

    if i == 1:
        for j in range(M):
            dp[i][j] = A[j] * (j + 1) - S[j]

    else:
        que = deque()
        que.append( (-1, 0, -0x7fffffff) )  # (i下标,dp[i][j], 凸包下边界上的斜率)

        for j in range(M):
            cur_k = A[j]
            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]

            k = que[pos][0]
            dp[i][j] = S[k] + dp[i-1][k] - A[j]*k - S[j] + j*A[j]

            # 更新凸包边界
            last_k = 0

            if len(que) > 1:
                while len(que) > 1:
                    k = que[-2][0]
                    last_k = ((S[j]+dp[i-1][j]) - (S[k]+dp[i-1][k])) / (j - k)
                    if que[-1][2] >= last_k:
                        que.pop()
                    else:
                        break

            k = que[-1][0]
            last_k = ((S[j] + dp[i-1][j]) - (S[k] + dp[i-1][k])) / (j - k)

            que.append((j, dp[i][j], last_k))

            # 把队列头的斜率偏小的点删掉,因为这个题目cur_k是递增的,所以斜率偏小的那些点对后续求解已经没作用了
            while len(que) > 1 and que[0][2] < selected_k:
                que.popleft()

print(dp[P][M-1])

这题二维的凸包优化DP,还真有点难度,数学变换有点绕

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页