为了账号安全,请及时绑定邮箱和手机立即绑定

朴素贪心算法入门指南:一步步教你掌握局部最优解

标签:
杂七杂八
1. 朴素贪心算法初探 - 贪心算法基本概念与朴素贪心算法的定义与特点

贪心算法是一种策略,其核心思想是在每个决策点上选择局部最优解,期望通过这些局部最优解累计形成全局最优解。朴素贪心算法是其中一种相对简单直接的实现方式,其特点如下:

定义

朴素贪心算法在解决问题时,总是选择当前看起来最优的选择。该算法不需要回溯,且每次选择都尽可能地减少问题规模,旨在通过局部最优解达到全局最优解。

特点

  • 直接性:每一步决策都尽可能地简单直接,基于当前信息做出选择。
  • 局部最优:每一次决策都确保当前最优,但不保证全局最优解。
  • 不可逆:一旦做出选择就不能撤销,这要求在选择时深思熟虑,确保后续步骤的最优性。
2. 理解局部最优与全局最优 - 局部最优解的选择原则与全局最优解与贪心选择的关系

局部最优是指在当前可以做出的选择中,选择的最优解。贪心算法在选择局部最优解时,基于一个假设:局部最优解会导出全局最优解。然而,这个假设在所有情况下均成立是其局限性之一。

实例:最小硬币找零问题

考虑找零15元,可用硬币面额为1元、2元、5元。贪心策略如下:

  • 步骤
    1. 从面额最小的硬币开始,选择尽可能多的硬币面额。
    2. 15元可以通过5元硬币×3,完成找零。

在最小硬币找零问题中,贪心算法直接选择面额最大的可用硬币,这样可以确保找到的硬币总数最少。这是因为硬币面额的组合性质使得每次选择最大面额的硬币是局部最优解,且最终为全局最优解。

3. 朴素贪心算法典型应用 - 最小硬币找零问题、活动安排问题、最优装载问题

最小硬币找零问题

def min_coins(n, coins):
    dp = [float('inf')] * (n + 1)
    dp[0] = 0
    for coin in coins:
        for i in range(coin, n + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[n] if dp[n] != float('inf') else -1

coins = [1, 2, 5]
n = 15
print(min_coins(n, coins))

活动安排问题

def max_activities(start, finish, n):
    result = []
    result.append(start[0])
    for i in range(1, n):
        if start[i] >= finish[result[-1]]:
            result.append(i)
    return result

start = [1, 3, 0, 5, 8, 5]
finish = [2, 4, 6, 7, 9, 9]
n = len(start)
print(max_activities(start, finish, n))

最优装载问题

from typing import List

def max_weight(n: int, weights: List[int], capacity: int) -> int:
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(1, capacity + 1):
            if weights[i - 1] <= w:
                dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + weights[i - 1])
            else:
                dp[i][w] = dp[i - 1][w]
    return dp[n][capacity]

weights = [1, 3, 4, 5]
n = len(weights)
capacity = 7
print(max_weight(n, weights, capacity))
4. 实战演练:手写一个朴素贪心算法示例 - 问题描述与分析、逐步构建算法流程、代码实现与测试

问题描述

编写一个程序,计算一个字符串中的最大数量的非重叠子字符串,每个子字符串都以元音字母开头。

分析

问题的关键在于找到每个子字符串中的元音字母作为起始点,并确保这些子字符串不重叠。

算法流程

  1. 初始化指针和答案变量。
  2. 遍历字符串,寻找每个元音字母作为起始点的子字符串。
  3. 更新答案变量,记录最大数量的非重叠子字符串。
  4. 返回答案变量的值。

代码实现

def max_vowel_substrings(s):
    vowels = 'aeiou'
    max_count = 0
    start = 0
    for end in range(len(s)):
        if s[end] in vowels:
            current_count = 0
            while end < len(s) and s[end] in vowels:
                current_count += 1
                end += 1
            max_count = max(max_count, current_count)
        elif s[start] in vowels:
            start += 1
    return max_count

s = "aaeiiouae"
print(max_vowel_substrings(s))
5. 避免陷阱:朴素贪心算法的局限性 - 何时不能使用贪心策略、分析失败案例与原因

失败案例分析

案例:0/1背包问题

  • 问题:给定一个背包容量和一系列物品,每个物品都有重量和价值,如何选择物品放入背包以获得最大价值。
  • 贪心策略:首先选择价值最大或价值/重量比最大的物品。
  • 失败原因:贪心策略可能无法保证全局最优解,因为可能会导致在选择高价值物品时,牺牲了整体价值的提升。例如,在有限的背包容量下,贪心策略可能会选择一系列较轻但价值低的物品,而放弃了更重、价值更高的物品。

总结

贪心算法虽然简单高效,但在某些情况下可能会受限于问题的复杂性而无法找到全局最优解。识别问题是否适合采用贪心策略,需要深入理解问题本身的性质和贪心策略的适用范围。

6. 进阶思考:朴素贪心与其他算法的结合 - 贪心与动态规划的比较、如何识别适合贪心算法的问题

贪心与动态规划的比较

  • 贪心算法:适用于能做出局部最优选择且这些选择最终导向全局最优解的问题。
  • 动态规划:适用于通过子问题的最优解构建全局最优解的问题,常见于解决具有重叠子问题和最优子结构的问题。

如何识别适合贪心算法的问题

  1. 问题分解:能否将问题分解为多个子问题?
  2. 局部最优:子问题的最优解能否组合成全局最优解?
  3. 无后效性:在每个决策点上,当前状态只依赖于当前决策,而不是前一个决策的状态。

识别问题是否适合采用贪心算法,关键在于是否能证明局部最优的选择在所有情况下都能导出全局最优解。

结语

通过深入理解和应用朴素贪心算法,您可以解决众多实际问题,提高编程效率。掌握贪心算法的同时,也了解其局限性,对于提升算法选择的灵活性和问题解决能力具有重要意义。不断实践和探索,您将能够更熟练地运用贪心策略,解决更复杂的问题。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消