1. 背包问题(部分背包)
问题:你有一个容量为 W 的背包,有很多物品,每个物品都有自己的重量和价值。你可以切割物品,也就是可以只拿走一个物品的一部分。如何装背包才能使总价值最大化?
贪心策略:
计算每个物品的单位价值(价值/重量)。
将所有物品按照单位价值从高到低排序。
从单位价值最高的物品开始装入背包,直到装满为止。
为什么是贪心?
在每一步,我们都选择当前“最划算”的物品(单位价值最高),因为我们希望以最快的速度增加背包的总价值。对于部分背包问题,这个贪心策略是正确的,因为可以切割物品,总是能把价值最高的部分装进去。
注意:如果是0/1背包问题(物品不能切割,要么全拿,要么不拿),贪心算法就不适用了。因为局部最优(选择单位价值最高的物品)可能无法推导出全局最优。这种问题需要用动态规划解决。
2. 活动安排问题
问题:有一系列活动,每个活动都有开始时间和结束时间。你有一个会场,在同一时间只能举办一个活动。如何安排活动,才能使举办的活动数量最多?
贪心策略:
将所有活动按照结束时间从早到晚排序。
选择第一个活动(结束时间最早的那个)。
接着,选择下一个开始时间不早于已选活动结束时间的活动。
重复这个过程,直到没有更多活动可选。
为什么是贪心?
我们每一步都选择结束时间最早的活动。这个“贪心”选择留出了尽可能多的时间给后续的活动,使得我们可以安排更多的活动。这是典型的局部最优导向全局最优的例子。
3. 霍夫曼编码(Huffman Coding)
问题:给定一组字符和它们出现的频率,为每个字符设计一个二进制前缀码,使得总编码长度最短。
贪心策略:
将所有字符看作独立的节点,它们的频率作为权重。
在所有节点中,选择频率最小的两个节点。
将这两个节点合并成一个新的父节点,新节点的频率是两个子节点的频率之和。
将新节点放回列表中,并重复上述过程,直到只剩下一个根节点。
为什么是贪心?
在每一步,我们都“贪婪”地选择频率最小的两个字符进行合并。这个局部最优的选择确保了频率最高的字符被分配到最短的编码,而频率最低的字符被分配到最长的编码,从而使得总编码长度最短。
霍夫曼编码到底在做什么?
一句话概括:霍夫曼编码是一种压缩数据的方法。它通过给最常出现的字符分配最短的编码,给最不常出现的字符分配最长的编码,从而让整个文件变得更小。
你可以把它想象成:你是一家公司,要给员工发名片。
你公司里有 5 个部门:人事部, 销售部, 技术部, 财务部, 行政部。
你发现 销售部 的人最多,技术部 的人其次,人事部 和 财务部 的人比较少,行政部 的人最少。
为了省钱和方便,你决定给他们分配简称。你会给谁一个很短的简称(比如“销”)?当然是人最多的销售部。而人最少的行政部,即使简称长一点(比如“行管”),也没关系。
霍夫曼编码就是这个道理:“人多”的用短称呼,“人少”的用长称呼。
4. Prim 算法和 Kruskal 算法(最小生成树)
问题:在一个连通的带权图中,选择一些边,使得所有顶点都能被连接起来,并且这些边的总权重最小。
贪心策略:
Prim 算法:从任意一个顶点开始,每一步都“贪婪”地选择一条连接当前已选顶点集合和未选顶点集合的、权重最小的边。
Kruskal 算法:每一步都“贪婪”地选择一条权重最小的边,只要这条边不会形成一个环。
为什么是贪心?
这两种算法在每一步都选择当前能得到的最佳边(权重最小的边),并最终构建出一个最小生成树。局部最优的选择(选择最小权重边)能保证最终得到全局最优解(总权重最小)。