收集巧克力(LeetCode日记)

LeetCode-2735-收集巧克力

题目信息:

给你一个长度为 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。

假设你可以执行任意次操作,请返回收集所有类型巧克力所需的最小成本。

  • 示例1:

输入: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 。可以证明这是一种最优方案。

  • 示例2:

输入:nums = [1,2,3], x = 4

输出:6

解释:我们将会按最初的成本收集全部三个类型的巧克力,而不需执行任何操作。因此,收集所有类型的巧克力需要的总成本是 1 + 2 + 3 = 6 。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 109
  • 1 <= x <= 109

相关标签 :数组、枚举、动态规划

题解

今天的问题属于中等难度,初见这道题,我认为动态规划将是一个很好的思路。对于每次操做,都可以比较当前情况下,直接收集权值最小的蛋糕 或者 先进行操做在进行购买权值最小的蛋糕。可是尝试后发现这样并不成功。下面我们来看一下正确的方法。

方法:枚举收集的次数

对于初始类型为 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) 的最小值。

实现代码(Python)
python 复制代码
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
复杂度分析:
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)。
  • 空间复杂度: O ( n ) O(n) O(n) 。

题记:


  • 研究生在读,我会尽量保持LeetCode每日一题的思路和代码输出。希望大家多多支持。
  • 水平有限,希望各位大佬能够批评指正。您的教诲是我进步的船帆。
  • 希望各位跟我一样的小白能跟我一起参与到做题和讨论中来。共同进步是我所能期盼的最高愿想。
  • 您的点赞和关注是我坚持分享的动力泉源,希望能将这件简单平凡的事一直做下去。感谢大家。
相关推荐
ChoSeitaku25 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__13531 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
Fuxiao___34 分钟前
不使用递归的决策树生成算法
算法
我爱工作&工作love我39 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower1 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui11 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农2 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
前端郭德纲2 小时前
浏览器是加载ES6模块的?
javascript·算法