文章目录
一、这个算法有点"贪"!
每次听到"贪心算法"这个词,我总会想起自助餐场景——每次都挑最贵的菜拿(虽然最后可能吃不完)。这个算法就像个"聪明的吃货",总是在当前步骤做出最优选择!(划重点)不过要注意,局部最优不等于全局最优哦!就像吃自助餐拿了太多龙虾导致没肚子吃和牛一样(血泪教训)。
二、什么时候该"贪"?
1. 高频应用场景(敲黑板!)
- 找零钱问题(老板总要准备最少硬币)
- 最小生成树(普里姆算法就是典型)
- 哈夫曼编码(压缩文件必备)
- 任务调度(如何高效利用时间)
2. 适用条件(超级重要!)
- 贪心选择性质:局部最优能导向全局最优
- 最优子结构:问题可分解为子问题的最优解
- 无后效性:选择后不会影响之前的状态
三、C++实战:背包问题
经典案例:部分背包问题
假设你是劫匪(纯属虚构!),背包容量50kg,宝物清单:
宝物 | 重量(kg) | 价值(万) |
---|---|---|
黄金 | 10 | 60 |
翡翠 | 20 | 100 |
钻石 | 30 | 120 |
贪心策略代码实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Treasure {
double weight;
double value;
double ratio; // 价值重量比
};
bool compare(Treasure a, Treasure b) {
return a.ratio > b.ratio; // 按价值比降序排列
}
double greedyKnapsack(int capacity, vector<Treasure>& treasures) {
sort(treasures.begin(), treasures.end(), compare);
double totalValue = 0.0;
for(auto& t : treasures) {
if(capacity >= t.weight) {
totalValue += t.value;
capacity -= t.weight;
} else {
totalValue += t.ratio * capacity;
break;
}
}
return totalValue;
}
int main() {
vector<Treasure> treasures = {
{10, 60}, {20, 100}, {30, 120}
};
// 计算价值重量比
for(auto& t : treasures) {
t.ratio = t.value / t.weight;
}
int capacity = 50;
double maxValue = greedyKnapsack(capacity, treasures);
cout << "最大价值:" << maxValue << "万" << endl; // 输出:240万
return 0;
}
代码解读(重点!)
- 结构体
Treasure
存储宝物属性 compare
函数实现自定义排序规则- 预处理阶段计算价值重量比
- 主算法循环处理每个宝物:
- 能装下就全拿
- 装不下就按比例拿
- 时间复杂度:O(n log n)(主要在排序)
四、为什么有人骂它"目光短浅"?
贪心算法的局限性
- 不能保证全局最优(比如0-1背包问题)
- 对问题结构要求严格
- 需要严格的数学证明(否则可能翻车)
避坑指南(亲测有效!)
- 先验证是否满足贪心条件
- 尝试构造反例测试
- 结合动态规划使用(比如Dijkstra算法)
- 当问题具有拟阵结构时可放心使用
五、进阶技巧:如何设计自己的贪心策略
3大设计方向
- 价值最大化:每次选价值最高的
- 代价最小化:每次选成本最低的
- 比例优化:综合多个参数(如价值/重量)
实战心得(血泪经验)
- 多打印中间结果调试(cout大法好!)
- 使用
priority_queue
优化排序 - 注意浮点数精度问题(比如比较时用epsilon)
- 活用STL的sort自定义比较函数
六、面试必问:贪心 vs 动态规划
对比表格(背下来!)
特征 | 贪心算法 | 动态规划 |
---|---|---|
决策方式 | 局部最优 | 全局最优 |
时间复杂度 | 通常较低 | 一般较高 |
空间复杂度 | 通常O(1) | 需要存储状态 |
问题类型 | 优化问题 | 组合问题 |
回溯能力 | 无 | 有 |
经典案例 | 霍夫曼编码 | 背包问题 |
七、你以为这就结束了?
最近在LeetCode刷题时发现个有趣现象:很多看似动态规划的题,用贪心反而更高效!比如:
- 122.买卖股票的最佳时机 II
- 455.分发饼干
- 406.根据身高重建队列
(小声bb)其实算法没有绝对的好坏,就像螺丝刀和扳手,关键看使用场景。下次遇到问题时,不妨先问三个问题:
- 能不能贪?
- 怎么贪?
- 贪得对不对?
最后送大家一句算法界的至理名言:“Greedy is good, when properly applied.” (贪心虽好,可不要贪杯哦~)