算法设计模式之贪婪法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/webzhuce/article/details/85802450

贪婪法

      贪婪法(greedy algorithm),又称贪心算法,是寻找最优解问题的常用方法。这种方法模式一般将求解过程分成若干个步骤,在每个步骤都应用贪心原则,选择当前状态下最好的或最优的选择(局部最有利的选择),并以此希望最后堆叠的结果也是最好或最优的解。贪婪法的每次决策都以当前情况为基础并根据某个最优原则进行选择,不从整体上考虑其他各种可能的情况。
      贪婪法和动态规划法以及分治法一样,都需要对问题进行分解,定义最优解的子结构。但是贪婪法与其他方法最大的不同在于,贪婪法每一步选择完之后,局部最优解就确定了,不再进行回溯处理,也就是,每一步骤的局部最优解确定以后,就不再修改,直到算法结束。因为不进行回溯处理,贪婪法只是在很少的情况下可以得到真正的最优解。但是贪婪法简单高效,省去了为赵最优解可能需要的穷举操作,可以得到与最优解比较接近的近似最优解,通常作为其他算法的辅助算法使用。

贪婪法的基本思想

      贪婪法的基本设计思想有以下三个步骤:

  1. 建立对问题精确描述的数学模型,包括定义最优解的模型;
  2. 将问题分解为一系列子问题,同时定义子问题的最优解结构;
  3. 应用贪心原则确定每个子问题的局部最优解,并根据最优解的模型,用子问题的局部最优解堆叠出全局最优解。

背包问题

      假设一个可装35磅重东西的背包,我们可以装以下三个东西。
在这里插入图片描述
      背包可装35磅,而音响最贵,我们尽量装音响,最后背包没有空间装其他东西了。如下图:
在这里插入图片描述
      但是如果我们装笔记本电脑和吉他,总价将为3500美元。从这里,我们看出贪婪算法不一定能获得最优解,但非常接近。
      上面的0-1背包问题是一个贪婪法的经典例子:有N件物品和一个承重为C的背包(也可定义为体积),每件物品的重量是wi,价值是pi,求解将哪几件物品装入背包可使这些物品在重量总和不超过C的情况下价值总和最大。背包问题是此类组合优化的NP完全问题的统称,比如货箱装载问题、货船载物问题等,因问题最初来源于如何选择最合适的物品装在背包中而得名。这个问题隐含了一个条件,每个物品只有一件,也就是限定每件物品只能选择0个或1个,因此又被称为0-1背包问题。

示例演示

#include <iostream>   
#include <vector>
#include <algorithm>
using namespace std;

struct BagResult
{
	int sum_value = 0; //最终背包里物品的总价值
	int sum_weight = 0; //最终背包里物品的总价值
	vector<string> names;
};

struct Goods           //表示每件物品
{
	string name;
	int weight;
	int value;
	Goods(const string& name, int weight, int value)
	{
		this->name = name;
		this->weight = weight;
		this->value = value;
	}
};

bool MaxValue(const Goods& a, const Goods& b)
{
	return a.value > b.value;
}

int main()
{
	int total_weight = 35; //背包最多能装的重量
	/* 物品名称: 音响        笔记本电脑     吉他
	*  重量    : 30磅        20磅          15磅
	*  价值    : 3000美元    2000美元      1500美元
	*/
	vector<Goods> goodslist;
	goodslist.push_back({"音响", 30, 3000});
	goodslist.push_back({"吉他", 15, 1500});
	goodslist.push_back({"笔记本电脑", 20, 2000});

	//将物品按价值最大到最小排序
	sort(goodslist.begin(), goodslist.end(), MaxValue);
	cout << "Sort Result:" << endl;
	for(auto& goods : goodslist)
		cout << goods.name.c_str() << endl;

	BagResult result;
	for(auto& goods : goodslist){
		if(result.sum_weight + goods.weight > total_weight)
			break;
		result.sum_weight += goods.weight;
		result.sum_value += goods.value;
		result.names.push_back(goods.name);
	}
	cout << "Greedy Algorithm Result:" << endl;
	for(auto& name : result.names)
		cout << name.c_str() << endl;
	system("pause");
}

运行结果

在这里插入图片描述

总结

      贪婪算法的优点就是简单易行,每步都采取最优的做法。用专业术语说,就是你每步都选择局部最优解,最终得到的就是全局最优解。

参考资料

  • 《算法图解》[M]
  • 《算法的乐趣》[M]

猜你喜欢

转载自blog.csdn.net/webzhuce/article/details/85802450