给你一个长度为 n n n 、下标从 0 0 0 开始的整数数组 n u m s nums nums ,表示收集不同巧克力的成本。每个巧克力都对应一个不同的类型,最初,位于下标 i i i 的巧克力就对应第 i i i 个类型。
在一步操作中,你可以用成本 x x x 执行下述行为:
同时修改所有巧克力的类型,将巧克力的类型 i t h i^{th} ith 修改为类型 ( ( i + 1 ) m o d n ) t h ((i + 1) mod n)^{th} ((i+1)modn)th。
假设你可以执行任意次操作,请返回收集所有类型巧克力所需的最小成本。
输入:nums = [20,1,15], x = 5
输出:13
解释:最开始,巧克力的类型分别是 [0,1,2] 。我们可以用成本 1 购买第 1 个类型的巧克力。
接着,我们用成本 5 执行一次操作,巧克力的类型变更为 [1,2,0] 。我们可以用成本 1 购买第 2 个类型的巧克力。
然后,我们用成本 5 执行一次操作,巧克力的类型变更为 [2,0,1] 。我们可以用成本 1 购买第 0 个类型的巧克力。
因此,收集所有类型的巧克力需要的总成本是 (1 + 5 + 1 + 5 + 1) = 13 。可以证明这是一种最优方案。
输入:nums = [1,2,3], x = 4
输出:6
解释:我们将会按最初的成本收集全部三个类型的巧克力,而不需执行任何操作。因此,收集所有类型的巧克力需要的总成本是 1 + 2 + 3 = 6 。
相关标签 :数组、枚举、动态规划
今天的问题属于中等难度,初见这道题,我认为动态规划将是一个很好的思路。对于每次操做,都可以比较当前情况下,直接收集权值最小的蛋糕 或者 先进行操做在进行购买权值最小的蛋糕。可是尝试后发现这样并不成功。下面我们来看一下正确的方法。
对于初始类型为 i i i 的巧克力,如果我们一共进行了 k k k kkk kkk 次操作,相当于我们可以用: n u m s [ i ] , n u m s [ ( i + 1 ) m o d n ] , ⋯ , n u m s [ ( i + k ) m o d n ] ( 1 ) nums[i],nums[(i+1)modn],⋯,nums[(i+k)modn] \ \ \ \ \ \ \ \ \ \ \ \ (1) nums[i],nums[(i+1)modn],⋯,nums[(i+k)modn] (1)
中的任意成本去购买它。由于我们希望成本最小,那么我们一定选择上述 k + 1 k+1 k+1 个成本中的最小值。同时我们发现,当进行了第 n n n 次操作后,它的价格又会回到 n u m s [ i ] nums[i] nums[i] 并继续开始循环,因此操作的次数不会超过 n − 1 n−1 n−1 。
这样一来,我们就可以枚举操作次数了,它的范围为 [ 0 , n − 1 ] [0,n−1] [0,n−1] 。当操作次数为 k k k 时,初始类型为 i i i 的巧克力需要的成本就是 ( 1 ) (1) (1) 中的最小值,我们就可以使用一个二维数组 f ( i , k ) f(i,k) f(i,k) 记录该值,它有如下的递推式:
{ f ( i , 0 ) = n u m s [ i ] f ( i , k ) = min { f ( i , k − 1 ) , n u m s [ ( i + k ) m o d n ] } \left\{\begin{array}{l}f(i, 0)=n u m s[i] \\f(i, k)=\min \{f(i, k-1), n u m s[(i+k) \bmod n]\}\end{array}\right. {f(i,0)=nums[i]f(i,k)=min{f(i,k−1),nums[(i+k)modn]}
即 f ( i , k ) f(i,k) f(i,k) 相较于 f ( i , k − 1 ) f(i,k−1) f(i,k−1) ,多了一个 n u m s [ ( i + k ) m o d n ] nums[(i+k) mod n] nums[(i+k) mod n] 的选择。此时,操作次数为 k k k 时的最小成本即为:
k ⋅ x + ∑ i = 0 n − 1 f ( i , k ) ( 2 ) k \cdot x+\sum_{i=0}^{n-1} f(i, k) \ \ \ \ \ \ \ \ \ \ \ \ (2) k⋅x+i=0∑n−1f(i,k) (2)
最终的答案即为所有 k ∈ [ 0 , n − 1 ] k∈[0,n−1] k∈[0,n−1] 中式 ( 2 ) (2) (2) 的最小值。
class Solution:
def minCost(self, nums: List[int], x: int) -> int:
n = len(nums)
f = nums[:] # 初始化 f 数组,表示当前位置开始的最小成本
ans = sum(f) # 初始化结果为 f 的总和
for k in range(1, n):
# 更新 f 数组,每次将当前位置的值更新为当前位置和右侧位置的最小值
for i in range(n):
f[i] = min(f[i], nums[(i + k) % n])
# 更新结果,计算当前成本并加上操作次数乘以 x
ans = min(ans, k * x + sum(f))
return ans