资讯中心 Info
当前位置:酷叮猫 > 资讯中心 >
贪心算法
发布日期:2018-08-17 阅读次数:0

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

 

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

 

思想

贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止

 

过程

建立数学模型来描述问题;

把求解的问题分成若干个子问题;

对每一子问题求解,得到子问题的局部最优解;

把子问题的解局部最优解合成原来解问题的一个解。                           

 

假设山洞中有 n 种宝物,每种宝物有一定重量 w 和相应的价值 v,毛驴运载能力有限,

只能运走 m 重量的宝物,一种宝物只能拿一样,宝物可以分割。那么怎么才能使毛驴运走宝物的价值最大呢?

我们可以尝试贪心策略:

(1)每次挑选价值最大的宝物装入背包,得到的结果是否最优?

(2)每次挑选重量最小的宝物装入,能否得到最优解?

(3)每次选取单位重量价值最大的宝物,能否使价值最高?

思考一下,如果选价值最大的宝物,但重量非常大,也是不行的,因为运载能力是有限

的,所以第 1 种策略舍弃;如果选重量最小的物品装入,那么其价值不一定高,所以不能在总重限制的情况下保证价值最大,第 2 种策略舍弃;而第 3 种是每次选取单位重量价值最大的宝物,也就是说每次选择性价比(价值/重量)最高的宝物,如果可以达到运载重量 m,那么一定能得到价值最大。

因此采用第 3 种贪心策略,每次从剩下的宝物中选择性价比最高的宝物。

 

算法设计

(1)数据结构及初始化。将 n 种宝物的重量和价值存储在结构体 three(包含重量、价

值、性价比 3 个成员)中,同时求出每种宝物的性价比也存储在对应的结构体 three 中,将其按照性价比从高到低排序。采用 sum 来存储毛驴能够运走的最大价值,初始化为 0。

(2)根据贪心策略,按照性价比从大到小选取宝物,直到达到毛驴的运载能力。每次选

择性价比高的物品,判断是否小于 m(毛驴运载能力),如果小于 m,则放入,sum(已放入物品的价值)加上当前宝物的价值,m 减去放入宝物的重量;如果不小于 m,则取该宝物的一部分 m * p[i],m=0,程序结束。m 减少到 0,则 sum 得到最大值。

 

完美图解

假设现在有一批宝物,价值和重量如表 2-3 所示,毛驴运载能力 m=30,那么怎么装入

最大价值的物品?

表 2-3  宝物清单

宝物 i  1  2  3  4  5  6  7  8  9  10

重量 w[i]  4  2  9  5  5  8  5  4  5  5

价值 v[i]  3  8  18  6  8  20  5  6  7  15

  1. 因为贪心策略是每次选择性价比(价值/重量)高的宝物,可以按照性价比降序排

序,排序后如表 2-4 所示。

表 2-4  排序后宝物清单

宝物 i  2  10  6  3  5  8  9  4  7  1

重量 w[i]  2  5  8  9  5  4  5  5  5  4

价值 v[i]  8  15  20  18  8  6  7  6  5  3

性价比 p[i]  4  3  2.5  2  1.6  1.5  1.4  1.2  1  0.75

 

  1. 按照贪心策略,每次选择性价比高的宝物放入:

第 1 次选择宝物 2,剩余容量 302=28,目前装入最大价值为 8。

第 2 次选择宝物 10,剩余容量 285=23,目前装入最大价值为 8+15=23。

第 3 次选择宝物 6,剩余容量 238=15,目前装入最大价值为 23+20=43。

第 4 次选择宝物 3,剩余容量 159=6,目前装入最大价值为 43+18=61。

第 5 次选择宝物 5,剩余容量 65=1,目前装入最大价值为 61+8=69。

第 6 次选择宝物 8,发现上次处理完时剩余容量为 1,而 8 号宝物重量为 4,无法全部

放入,那么可以采用部分装入的形式,装入 1 个重量单位,因为 8 号宝物的单位重量价值为

1.5,因此放入价值 1×1.5=1.5,你也可以认为装入了 8 号宝物的 1/4,目前装入最大价值为69+1.5=70.5,剩余容量为 0。

 

  1. 构造最优解

把这些放入的宝物序号组合在一起,就得到了最优解(2,10,6,3,5,8),其中最后

一个宝物为部分装入(装了 8 号财宝的 1/4),能够装入宝物的最大价值为 70.5。

 

伪代码详解

(1)数据结构定义

根据算法设计中的数据结构,我们首先定义一个结构体 three:

struct three{

double w; //每种宝物的重量

double v; //每种宝物的价值

double p; //每种宝物的性价比(价值/重量)

 

(2)性价比排序

我们可以利用 C++中的排序函数 sort(见附录 B),对宝物的性价比从大到小(非递增)

排序。要使用此函数需引入头文件:

#include

语法描述为:

sort(begin, end)// 参数 begin 和 end 表示一个范围,分别为待排序数组的首地址和尾地址

在本例中我们采用结构体形式存储,按结构体中的一个字段,即按性价比排序。如果不

使用自定义比较函数,那么 sort 函数排序时不知道按哪一项的值排序,因此采用自定义比较函数的办法实现宝物性价比的降序排序:

bool cmp(three a,three b)//比较函数按照宝物性价比降序排列

{

return a.p > b.p; //指明按照宝物性价比降序排列

}

sort(s, s+n, cmp); //前两个参数分别为待排序数组的首地址和尾地址

//最后一个参数 compare 表示比较的类型

 

(3)贪心算法求解

在性价比排序的基础上,进行贪心算法运算。如果剩余容量比当前宝物的重量大,则可以放入,剩余容量减去当前宝物的重量,已放入物品的价值加上当前宝物的价值。如果剩余

容量比当前宝物的重量小,表示不可以全部放入,可以切割下来一部分(正好是剩余容量),

然后令剩余容量乘以当前物品的单位重量价值,已放入物品的价值加上该价值,即为能放入

宝物的最大价值。

for(int i = 0;i < n;i++)//按照排好的顺序,执行贪心策略

{

if( m > s[i].w )//如果宝物的重量小于毛驴剩下的运载能力,即剩余容量

{

m -= s[i].w;

sum += s[i].v;

}

else //如果宝物的重量大于毛驴剩下的承载能力

{

sum += m 乘以 s[i].p; //进行宝物切割,切割一部分(m 重量),正好达到驴子承重

break;

}

}