贪心算法
贪心算法 (Greedy Algorithm)
像“捡芝麻”一样,目光短浅,每一步都只做当前看起来最好的选择(局部最优)。一旦做出选择就绝不反悔,期望通过一系列局部最优拼凑出全局最优。
经典问题推演:找零钱问题
问题:用面值 {10, 5, 1} 的硬币凑出 18 元,要求硬币数量最少。
贪心策略:每次优先选面值最大的硬币。
执行过程:
选 10,剩余 8。
剩下的最大是 5,选 5,剩余 3。
剩下的最大是 1,连选 3 个 1,剩余 0。
结果:共 5 枚硬币。
活动选择问题 (Activity Selection Problem)
问题描述:假设有一个教室,需要在一天内安排尽可能多的活动。给定 n 个活动,每个活动都有一个开始时间和一个结束时间。同一个教室在同一时间只能进行一个活动(即活动时间不能重叠)。请问最多能安排多少个活动?
示例:输入活动列表 [(1,4), (3,5), (0,6), (5,7), (8,9)](括号内为开始和结束时间),输出 3(因为可以选择 (1,4)、(5,7) 和 (8,9) 这三个互不重叠的活动)。
为什么用贪心算法?
如果用暴力穷举或动态规划,我们需要考虑所有可能的组合,时间复杂度极高。而贪心算法告诉我们:不需要全局搜索,只要每一步都做出当前看起来最好的选择(局部最优),就能顺理成章地达到全局最优。
核心思想:贪心策略的选择
对于区间调度问题,贪心的策略是:每次优先选择“结束时间最早”的活动。
直觉理解:越早结束,留给后续活动的时间就越多。
数学支撑:贪心算法要求问题具备“贪心选择性质”和“最优子结构”。可以严格证明:如果存在一个最优解,它一定包含当前结束时间最早的那个活动。
核心代码
#include <stdio.h>
// 定义活动结构体
typedef struct {
int start;
int end;
} Activity;
// 贪心算法求解活动选择问题
int activitySelection(Activity activities[], int n) {
// 1. 贪心策略:按结束时间升序排序(假设排序已完成,此处省略排序代码)
// sort(activities, n);
int count = 1; // 至少可以安排第一个(结束最早)的活动
int lastEnd = activities[0].end; // 记录上一个已选活动的结束时间
// 2. 迭代做出贪心选择
for (int i = 1; i < n; i++) {
// 如果当前活动的开始时间 >= 上一个活动的结束时间,说明不重叠
if (activities[i].start >= lastEnd) {
count++; // 做出选择,绝不反悔
lastEnd = activities[i].end; // 更新最后结束时间
}
}
return count;
}
int main() {
Activity activities[] = {{1,4}, {3,5}, {0,6}, {5,7}, {8,9}};
int n = sizeof(activities) / sizeof(activities[0]);
printf("最多可安排的活动数为:%d\n", activitySelection(activities, n));
return 0;
}
示例推演
以活动列表 [(1,4), (3,5), (0,6), (5,7), (8,9)] 为例:
第 1 步:预处理(排序)
目标:找出当前状态下的局部最优。
将所有活动按结束时间升序排列。排序后顺序不变:(1,4), (3,5), (0,6), (5,7), (8,9)。
第 2 步:第一次贪心选择
目标:选出第一个活动。
扫描列表,第一个活动是 (1,4)。
做出选择:安排 (1,4)。
更新状态:当前已占用时间到 4(lastEnd = 4),已选数量 count = 1。
第 3 步:第二次贪心选择
目标:在剩余活动中找下一个不冲突的。
检查 (3,5):开始时间 3 < 4,冲突,跳过。
检查 (0,6):开始时间 0 < 4,冲突,跳过。
检查 (5,7):开始时间 5 >= 4,不冲突!
做出选择:安排 (5,7)。
更新状态:当前已占用时间到 7(lastEnd = 7),已选数量 count = 2。
第 4 步:第三次贪心选择
目标:继续向后扫描。
检查 (8,9):开始时间 8 >= 7,不冲突!
做出选择:安排 (8,9)。
更新状态:当前已占用时间到 9(lastEnd = 9),已选数量 count = 3。
最终结果
遍历结束,最多可安排 3 个活动。
两大核心性质:
贪心选择性质:通过局部最优选择(选结束最早的),可以推导出全局最优解。
最优子结构性质:原问题的最优解包含了其子问题的最优解。
贪心 vs 动态规划:
贪心是自顶向下⬇️的迭代,做出选择后绝不反悔(无回溯),效率极高。
动态规划是自底向上⬆️的填表,会考虑所有子问题的组合。

浙公网安备 33010602011771号