贪心算法详解与C++实战:如何做出“当下最优”的选择
贪心算法,就像在迷宫中每次只选择当前看起来最短的那条路——它不保证全局最优,但常常能高效地找到不错的解决方案。
在算法世界里,贪心算法以其直观、高效的特点占据着重要地位。它不像动态规划那样需要考虑所有子问题,也不像回溯算法那样需要尝试所有可能。贪心算法的核心思想是:每一步都做出在当前看来最好的选择,希望通过局部最优的选择达到全局最优。
本文将带你深入理解贪心算法,并通过C++代码实例展示如何应用这一强大的算法思想。
什么是贪心算法?
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法策略。
核心特点:
局部最优选择: 每一步都选择当前最优的选项
不可撤销: 一旦做出选择,就不回头重新考虑
高效简单: 通常时间复杂度较低,实现相对简单
贪心算法适用于贪心选择性质和最优子结构的问题:
贪心选择性质: 通过局部最优选择能达到全局最优
最优子结构: 问题的最优解包含子问题的最优解
经典贪心问题与C++实现
问题1:活动选择问题
问题描述:有n个活动,每个活动有开始时间和结束时间。选择尽可能多的互不冲突的活动。
贪心策略:每次选择结束时间最早的活动,这样能为后续活动留出更多时间。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Activity {
int start; // 开始时间
int end; // 结束时间
int index; // 原始索引(便于输出)
Activity(int s, int e, int i) : start(s), end(e), index(i) {}
};
// 比较函数:按结束时间升序排序
bool compareActivities(const Activity& a, const Activity& b) {
return a.end < b.end;
}
void selectActivities(vector<Activity>& activities) {
if (activities.empty()) return;
// 步骤1:按结束时间排序
sort(activities.begin(), activities.end(), compareActivities);
cout << "选择的活动: ";
// 步骤2:贪心选择
int lastEnd = activities[0].end;
cout << "活动" << activities[0].index + 1 << " ";
for (int i = 1; i < activities.size(); i++) {
// 如果当前活动的开始时间 >= 上一个活动的结束时间,则选择
if (activities[i].start >= lastEnd) {
cout << "活动" << activities[i].index + 1 << " ";
lastEnd = activities[i].end;
}
}
cout << endl;
}
int main() {
vector<Activity> activities = {
Activity(1, 4, 0), // 活动1: 1-4
Activity(3, 5, 1), // 活动2: 3-5
Activity(0, 6, 2), // 活动3: 0-6
Activity(5, 7, 3), // 活动4: 5-7
Activity(3, 9, 4), // 活动5: 3-9
Activity(5, 9, 5), // 活动6: 5-9
Activity(6, 10, 6), // 活动7: 6-10
Activity(8, 11, 7), // 活动8: 8-11
Activity(8, 12, 8), // 活动9: 8-12
Activity(2, 13, 9), // 活动10: 2-13
Activity(12, 14, 10) // 活动11: 12-14
};
selectActivities(activities);
return 0;
}
问题2:分数背包问题
问题描述:给定n个物品和一个容量为W的背包。物品i有重量wi和价值vi。可以带走物品的一部分,求最大价值。
贪心策略:每次选择单位重量价值最高的物品(性价比最高)。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Item {
int weight; // 重量
int value; // 价值
double ratio; // 单位重量价值(性价比)
Item(int w, int v) : weight(w), value(v) {
ratio = (double)v / w;
}
};
// 比较函数:按性价比降序排序
bool compareItems(const Item& a, const Item& b) {
return a.ratio > b.ratio;
}
double fractionalKnapsack(int capacity, vector<Item>& items) {
// 按性价比降序排序
sort(items.begin(), items.end(), compareItems);
double totalValue = 0.0;
int remainingCapacity = capacity;
for (const Item& item : items) {
if (remainingCapacity <= 0) break;
if (item.weight <= remainingCapacity) {
// 整个物品都能装下
totalValue += item.value;
remainingCapacity -= item.weight;
} else {
// 只能装下一部分
double fraction = (double)remainingCapacity / item.weight;
totalValue += item.value * fraction;
remainingCapacity = 0;
}
}
return totalValue;
}
int main() {
vector<Item> items = {
Item(10, 60), // 物品1: 重量10, 价值60
Item(20, 100), // 物品2: 重量20, 价值100
Item(30, 120) // 物品3: 重量30, 价值120
};
int capacity = 50;
double maxValue = fractionalKnapsack(capacity, items);
cout << "背包容量: " << capacity << endl;
cout << "最大价值: " << maxValue << endl;
// 输出详细信息
cout << "物品详情:" << endl;
for (int i = 0; i < items.size(); i++) {
cout << "物品" << i + 1
<< ": 重量=" << items[i].weight
<< ", 价值=" << items[i].value
<< ", 性价比=" << items[i].ratio << endl;
}
return 0;
}
贪心算法的优缺点
优点:
高效简单:时间复杂度通常较低
易于实现:思路清晰,代码直观
空间效率高:通常不需要额外的数据结构
缺点:
可能得不到全局最优解:贪心选择不一定是全局最优
适用范围有限:只适用于具有贪心选择性质的问题
难以证明正确性:有时难以证明贪心策略的正确性
更多推荐

所有评论(0)