五大常用算法入门(一)——贪心算法

1.贪心算法简介
1.1 基本定义

贪婪算法(greedy method) 中,我们要逐步构造一个最优解。每一步,我们都在一定的标准下,做出一个最优决策。做出决策所依据的标准称为贪心准则(greedy criterion)

贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择
也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解

贪心算法每一步必须满足以下条件:
  1、可行的:即它必须满足问题的约束。
  2、局部最优:他是当前步骤中所有可行选择中最佳的局部选择。
  3、不可更改:即选择一旦做出,在算法的后面步骤就不可改变了。

注意:!!!
贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

1.2 贪心算法案例

钱币找零问题
假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来找给顾客K元,怎么用数目最少的钱来找零?

贪心准则:在不超过要找的零钱总数的条件下,每一次都选择面值尽可能大的纸币,直到凑成的零钱总数等于要找的零钱总数。

#include<iostream>

using namespace std;

int min(int a, int b) {
	return a < b ? a : b;
}
int main()
{
	//人民币面值集合
	int values[] = { 1, 2, 5, 10, 20, 50, 100 };
	//各种面值对应数量集合
	int counts[] = { 3, 1, 2, 1, 1, 3, 5 };
	//求442元人民币需各种面值多少张
	//用来记录需要的各种面值张数
	int money = 442;
	int len = sizeof(values) / sizeof(values[0]);
	int* result = new int[len];
	for (int i = len - 1; i >= 0; i--) {
		int num = 0; //当前面值纸币的数量
		num = min(money / values[i], counts[i]); //当前纸币可以找的最大数量
		money = money - num*values[i];
		result[i] = num;
	}
	//输出最后结果
	for (int i = 0; i < len; i++) {
		if(result[i])
			cout << "需要面额为" << values[i] << "的人名币" << result[i] << "张\n";
	}
	cout << endl;

	system("pause");
	return 0;
}

程序结果:

需要面额为2的人名币1张
需要面额为5的人名币2张
需要面额为10的人名币1张
需要面额为20的人名币1张
需要面额为100的人名币4张

可以得出,求出的结果为最优解,但是,当纸币面值和数量为某些特殊情况下,贪心算法就无法给出最优解。但是,贪心算法往往能给出近似解,对于我们来说也是有价值的。

比如对于纸币有1、5、7面值的若干,要凑出10元
贪心解[3,0,1]
最优解[0,2,0]

1.3.贪心算法的基本思路

1.建立数学模型来描述问题。
2.把求解的问题分成若干个子问题。
3.对每一子问题求解,得到子问题的局部最优解。
4.把子问题的解局部最优解合成原来解问题的一个解。

2.贪心算法最优性证明
2.1 贪心算法的前提

贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
关键是贪心策略的选择,而贪心算法动态规划的主要区别是:
贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。即贪心选择是采用从顶向下、以迭代的方法做出相继选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题
所以,贪心算法的正确性可以通过数学归纳法贪心交换来给予证明。

2.2 最优子结构

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
运用贪心策略在每一次转化时都取得了最优解。问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。

2.3 贪心算法与动态规划的区别
  • 贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。
  • 贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。
  • 能用贪心解决的问题,也可以用动态规划解决
3.贪心算法的经典问题
3.1 近似解
  • 0/1背包问题
3.2 最优解

未完待续。。。。

参考资料

1.《数据结构、算法与应用 C++描述》 第18章
2.《算法导论》

  • 182
    点赞
  • 809
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优的选择,从而希望最终导致全局最优的算法贪心算法通常有以下特点: 1. 贪心选择性质:每一步都采取最优的选择,即局部最优解。 2. 最优子结构性质:原问题的最优解包含子问题的最优解。 3. 无后效性:每一步的决策只与当前状态有关,与之前的决策无关。 贪心算法可以用来求解一些最优化问题,如最小生成树、背包问题、活动选择问题等。 以活动选择问题为例,假设有n个活动,每个活动有一个开始时间s和结束时间f,多个活动可能在同一时间段内进行,但是同一时间只能进行一个活动。如何选择活动,使得能够安排的活动数量最多? 贪心算法的思路是:按照结束时间从早到晚排序,每次选择结束时间最早的活动,且不与已经选择的活动时间冲突。这是因为选择结束时间最早的活动,可以给后面留下更多的时间选择其他活动,从而使得总的安排活动数量最多。 参考代码如下: ``` #include <iostream> #include <algorithm> using namespace std; struct Activity { int start, finish; }; bool compare(Activity s1, Activity s2) { return (s1.finish < s2.finish); } void printMaxActivities(Activity arr[], int n) { sort(arr, arr+n, compare); cout << "Selected activities:\n"; int i = 0; cout << "(" << arr[i].start << ", " << arr[i].finish << "), "; for (int j = 1; j < n; j++) { if (arr[j].start >= arr[i].finish) { cout << "(" << arr[j].start << ", " << arr[j].finish << "), "; i = j; } } } int main() { Activity arr[] = {{1, 2}, {3, 4}, {0, 6}, {5, 7}, {8, 9}, {5, 9}}; int n = sizeof(arr)/sizeof(arr[0]); printMaxActivities(arr, n); return 0; } ``` 输出结果为: ``` Selected activities: (1, 2), (3, 4), (5, 7), (8, 9), ``` 可以看到,按照贪心算法的思路,选择了4个活动,使得能够安排的活动数量最多。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值