返回

勇闯 LeetCode,硬币消消乐,妙招频出,尽在掌握

闲谈

序言

踏上算法竞赛的征途,LeetCode 是一座无法绕过的丰碑。其中一道经典题目——“拿硬币”,考验着算法爱好者的思维和策略。本文将深入剖析这道题目的巧妙解法,带你领略动态规划算法的强大魅力。

题目解析

“拿硬币”题目如下:

桌上摆放着 n 堆力扣币,每堆中硬币的数量分别存储在数组 coins 中。你每次可以执行以下两种操作之一:

  • 拿走当前堆中一枚硬币
  • 拿走当前堆中两枚硬币

你的目标是拿走所有硬币,并使拿取次数尽可能少。求出最少需要的拿取次数。

动态规划算法

解决这道题目,我们可以运用动态规划算法。动态规划算法是一种自底向上、逐步求解问题的技术,适用于具有“最优子结构”特征的问题。

在“拿硬币”问题中,我们可以定义状态 dp[i] 为拿完前 i 堆硬币所需的最小拿取次数。为了获得最优解,我们需要依次计算出 dp[1]、dp[2]、...、dp[n]。

状态转移方程

在计算 dp[i] 时,我们需要考虑两种情况:

  • 如果从第 i 堆硬币中拿走一枚硬币,则 dp[i] = dp[i-1] + 1
  • 如果从第 i 堆硬币中拿走两枚硬币,则 dp[i] = dp[i-2] + 1

具体步骤

根据状态转移方程,我们可以得到以下具体步骤:

  1. 初始化: dp[0] = 0,dp[1] = 1
  2. 循环递推: 对于 i 从 2 循环到 n,计算 dp[i]
    • 如果 coins[i-1] >= 2,则 dp[i] = min(dp[i-1] + 1, dp[i-2] + 1)
    • 否则,dp[i] = dp[i-1] + 1
  3. 返回结果: 返回 dp[n]

时间复杂度

该算法的时间复杂度为 O(n),其中 n 为硬币堆数。

代码示例(Python)

def minCount(coins):
    dp = [0] * (len(coins) + 1)
    dp[1] = 1
    for i in range(2, len(coins) + 1):
        if coins[i-1] >= 2:
            dp[i] = min(dp[i-1] + 1, dp[i-2] + 1)
        else:
            dp[i] = dp[i-1] + 1
    return dp[len(coins)]

总结

“拿硬币”问题看似简单,但蕴藏着动态规划算法的精妙之处。通过将问题分解成子问题,并逐一求解,我们能够高效地找到最优解。掌握动态规划算法,不仅能够解决竞赛中的难题,更能提升我们对算法设计和问题的抽象能力。