C++贪心算法
📚 C++ 中贪心算法详解
1. 贪心算法的定义
贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下局部最优的选择,以期最终达到全局最优解的算法。
1.1 核心思想
- 局部最优选择:在问题的每一步中,都选择当前看起来最优或最有利的选项。
- 全局最优目标:通过一系列局部最优的选择,最终达到全局最优或近似最优解。
- 不可回溯性:一旦做出选择,无法回头更改之前的选择。
1.2 贪心算法的适用条件
贪心算法并不总能保证得到全局最优解,它只在满足以下两个条件时才适用:
贪心选择性
- 可以通过每一步的局部最优选择,最终得到全局最优解。
最优子结构
- 问题的最优解包含其子问题的最优解。
1.3 贪心算法与其他算法的区别
- 动态规划:适合有重叠子问题的场景,结果需要基于多个子问题的解。
- 回溯算法:会考虑所有可能的选择,找到最佳解。
- 贪心算法:在每一步选择中做出最优决定,不再考虑之前的选择或之后的结果。
2. 贪心算法的基本步骤
- 问题分解:将问题分解为多个子问题。
- 贪心策略:针对每个子问题,选择当前最优解。
- 合并结果:将所有子问题的最优解组合起来,形成问题的最终解。
- 验证结果:判断最终解是否满足问题的要求。
3. 贪心算法的优缺点
✅ 优点:
- 简单直观,易于理解和实现。
- 在某些问题上,可以提供高效的最优解。
- 时间复杂度较低,通常比动态规划更快。
❌ 缺点:
- 并不适用于所有问题,可能无法得到全局最优解。
- 一旦选择某一步,无法回溯进行更改。
- 依赖问题是否满足贪心选择性和最优子结构。
4. 贪心算法的经典应用场景
- 活动选择问题(Activity Selection Problem)
- 哈夫曼编码(Huffman Coding)
- 最小生成树(Minimum Spanning Tree):Kruskal算法、Prim算法
- 单源最短路径问题(Shortest Path Problem):Dijkstra算法
- 背包问题(Fractional Knapsack Problem)
5. 贪心算法的经典例题
示例:分数背包问题(Fractional Knapsack Problem)
问题描述:
- 有一背包,容量为 ( W )。
- 有 ( n ) 个物品,每个物品有一个重量 ( w_i ) 和价值 ( v_i )。
- 可以将物品切分成任意部分放入背包。
- 目标:在不超过背包容量的前提下,使得背包中的总价值最大化。
贪心策略:
- 优先选择单位价值最高的物品。
C++ 实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 物品结构体
struct Item {
int weight;
int value;
};
// 比较单位价值
bool compare(Item a, Item b) {
double r1 = (double)a.value / a.weight;
double r2 = (double)b.value / b.weight;
return r1 > r2;
}
// 分数背包问题
double fractionalKnapsack(int W, vector<Item>& items) {
// 按单位价值排序
sort(items.begin(), items.end(), compare);
double totalValue = 0.0; // 背包中物品的总价值
for (auto& item : items) {
if (W >= item.weight) {
// 如果当前物品可以完全装入
W -= item.weight;
totalValue += item.value;
} else {
// 装入部分物品
totalValue += item.value * ((double)W / item.weight);
break; // 背包已满
}
}
return totalValue;
}
int main() {
int W = 50; // 背包容量
vector<Item> items = {{10, 60}, {20, 100}, {30, 120}};
double maxValue = fractionalKnapsack(W, items);
cout << "背包可以装入的最大价值为: " << maxValue << endl;
return 0;
}
📝 代码解析:
- 将物品按单位价值降序排序。
- 遍历物品:
- 如果物品可以完全装入背包,则加入背包。
- 如果不能完全装入,则加入部分物品。
- 返回背包中的总价值。
✅ 输出示例:
背包可以装入的最大价值为: 240
6. 贪心算法的总结
6.1 使用贪心算法的步骤
- 理解问题,判断是否满足贪心选择性和最优子结构。
- 明确每一步的贪心策略。
- 实现贪心算法,确保选择的局部最优解是合理的。
- 验证最终解是否是全局最优解。
6.2 贪心算法的思维方式
- 每次做出局部最优选择。
- 确保当前选择不会影响整体解的最优性。
- 结合实际问题,灵活设计贪心策略。
7. 常见的贪心算法题型
- 选择问题:活动选择问题
- 路径问题:Dijkstra 最短路径
- 构建问题:哈夫曼编码
- 分配问题:分数背包问题
贪心算法虽然简单高效,但需要仔细分析问题,确保满足贪心选择性和最优子结构。在适用场景下,贪心算法无疑是一种强大的工具。