序言
五大常用算法包括:回溯法 + 贪心算法 + 动态规划 + 分治法 + 分支限界法
之前已经介绍过回溯法及其在八皇后问题的应用。本文介绍贪心算法及其常见应用场景。
贪心算法
- 贪心算法的基本原理
问题求解时,不是从全局最优解来考虑,而是通过求解局部最优解来产生全局最优解或全局最优解的近似解。
概念要点:
1. 局部最优解
2. 无后效性,即不可回溯
贪心算法的特性:所求解的问题一般具有两个特性
[1] 贪心选择性质:整体最优可以通过一系列局部最优的选择来达到。
- 贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
[2] 最优子结构性质:一个问题的最优解已经包含了其子问题的最优解。
- 因此可以依次解决其子问题得到原问题的解
贪心算法的基本思路
- 建立数学模型
- 问题分成若干子问题
- 子问题求解得到子问题的局部最优解(以贪心选择开始)
- 子问题局部最优合成原问题的一个解
贪心算法使用的要点
问题是否可用贪心算法求解:经验判断,适当证明
- 贪心算法适用范围窄,很多时候需要根据经验决定是否选择贪心算法
- 通过数学归纳法进行证明,利用贪心选择性质和最优子结构性质
- 贪心标准的选择
- 很重要,标准选择不好很可能导致解题失败
- 在选择贪心标准时,我们要对所选的贪心标准进行验证才能使用,不要被表面上看似正确的贪心标准所迷惑
与动态规划问题的区别
- 相似:均通过递推实现,均具有最优子结构性质,均无后效性
- 区别:
- 贪心算法每一步的最优解一定包含上一步最优解。上一步最优解不做保留
- 动态规划全局最优一定包含某个局部最优,但不一定包含前一个局部最优。需要记录之前所有最优解
贪心算法常见应用场景
- 公共资源争用的活动安排
- 教室不同时间段活动安排
- 公共资源争用的活动安排
- 背包问题
- 不是”0-1背包问题“
- 背包问题
- 最优装载问题
- 与背包问题类似
- 最优装载问题
- 钱币找零问题
- 最少纸币张数支付K元
- 钱币找零问题
贪心算法应用举例
以背包问题为例简单举例
问题描述
背包问题:有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
价值 10 40 30 50 35 40 30贪心标准
- [1] 先把价值高的装进去:这样重量消耗太快,不利于总体价值的增长
- [2] 先把重量小的装进去:这样能保证重量有效增长,但价值不能有效增长
- [3] 先把价值重量比最大的装进去:按物品权重排序,实现重量和总价值的有效增长
代码(C)
#include <stdio.h>
#define true 1
#define false 0
typedef struct pack_node
{
int weight;
int value;
float value2weight; //价值重量比
char flag;
}packNode;
int main()
{
int Weight[7] = {35, 30, 60, 50, 40, 15, 20};
int Value[7] = {10, 40, 30, 50, 35, 40, 30};
packNode back_pack[7];
int i = 0;
for (; i < 7; i++)
{
back_pack[i].weight = Weight[i];
back_pack[i].value = Value[i];
back_pack[i].flag = false;
back_pack[i].value2weight = (float)((float)(Value[i]) / (float)Weight[i]);
}
int allWeight = 0;
int allValue = 0;
float max = 0.0; //value / weight最大比值
char markArr[7] = {0};
int j, eleFlag;
float portion;
while (allWeight < 150)
{
j = 0;
max = back_pack[j].value2weight;
while (j < 7)
{
if (back_pack[j].value2weight > max && back_pack[j].flag == false)
{
max = back_pack[j].value2weight;
eleFlag = j;
}
j++;
}
back_pack[eleFlag].flag = true;
allWeight += back_pack[eleFlag].weight;
allValue += back_pack[eleFlag].value;
markArr[eleFlag] = 1;
if (allWeight > 150)
{
portion = (back_pack[eleFlag].weight - (allWeight - 150)) / (float)back_pack[eleFlag].weight;
printf("第%d个物品选取:%.1f\n", eleFlag + 1, portion);
}
}
int k = 0;
printf("取出物品标号如下:");
for (; k < 7; k++)
printf("%d%c",markArr[k], k == 6 ? '\n' : ' ');
return 0;
}
Acknowledgements:
http://blog.csdn.net/liufeng_king/article/details/8709005
http://blog.csdn.net/liufeng_king/article/details/8711928
http://blog.csdn.net/qfikh/article/details/51959226
http://blog.csdn.net/qq_23100787/article/details/50490658
2017.09.14