贪心算法.docx
《贪心算法.docx》由会员分享,可在线阅读,更多相关《贪心算法.docx(41页珍藏版)》请在冰豆网上搜索。
贪心算法
贪心算法2008-10-1421:
20:
06|分类:
信息技术|标签:
|字号大中小订阅.
贪心算法
最优化问题是程序设计中一类非常重要的问题。
每一个最优化问题都包含一组约束条件和一个优化函数,满足约束条件的问题求解方案称为问题的可行解,使优化函数取得最优值的可行解称为问题的最优解。
贪心算法是解决最优化问题的一种基本方法。
它采用逐步构造最优解的思想,在问题求解的每一个阶段,都作出一个在一定标准下看上去最优的决策;决策一旦作出,就不可再更改。
制定决策的依据称为贪婪准则。
使用贪婪算法解决问题,通常需要做好以下几个方面的工作:
1、明确问题的求解目标。
2、分析问题所包含的约束条件。
3、建立优化函数。
优化函数通常可以通过综合分析问题的求解目标及约束条件归纳出来。
4、制定贪婪准则。
清楚问题的求解目标、所包含的约束条件及优化函数之后,就可以着手制定一个可行的贪婪准则。
贪婪准则的制定是用贪婪算法解决最优化问题的关键,它关系到问题能否得到成功解决及解决质量的高低。
把以上几个方面的工作都完成了,问题自然会不攻自破。
例1:
计算发放工资时所需的最少人民币张数。
输入数据为职工的工资数。
要求统计并输出应发给该职工面值100元、50元、20元、10元、5元、2元、1元的人民币各多少张,使总张数最少。
分析:
举个具体例子,假设某职工的工资为638元,我们该发给他多少张人民币呢?
答案可以是638张面值1元的人民币,可以是638/2=319张面值2元的人民币,……无论发给该职工人民币多少张,他拿到的人民币总和都应等于他自己的工资数。
如果以mz[i]表示第i种人民币的面值,zs[i]表示职工拿到的第i种人民币的张数,zggz表示职工的工资数,那么应有:
=zggz(根据题意,共有7种面值的人民币)。
这是问题的约束条件。
再阅读题目,我们发现问题的求解目标是“使总张数最少”。
相应的优化函数是职工拿到的人民币张数,即。
满足约束条件=zggz并使取得最优值(在这里为最小值)的求解方案就是问题的最优解。
要发给该职工638元的工资,并使总张数最少,直觉告诉我们,应先给他6张面值100元的人民币;第7张不能再给100元的,也不能给50元的,否则该职工实际拿到的工资将会超过他应得的工资;显然,第7张应为面值20元的人民币。
同理,第8张为面值10元的人民币,第9张为面值5元的人民币,第10张为面值2元的人民币,第11张为面值1元的人民币。
该职工共拿到人民币11张,分别为面值100元的6张,面值50元的没有,面值20元、10元、5元、2元和1元的各1张。
这样,不但满足了约束条件=zggz,即100×6+(20+10+5+2+1)×1=638,而且使该职工拿到的人民币张数最少。
把以上操作方法归纳出来就是一种可行的贪婪准则:
按面值从大到小的顺序分发人民币,并始终保证职工实际拿到的工资不超过他应得的工资。
参考程序如下:
dimmz(1to7)asinteger
dimzs(1to7)asinteger
cls:
inputzggz
mz
(1)=100:
mz
(2)=50:
mz(3)=20
mz(4)=10:
mz(5)=5:
mz(6)=2:
mz(7)=1
fori=1to7
zs(i)=0
nexti
i=1:
sy=zggz
rem变量sy用来记录在给某职工发工资的过程中,某一时刻还需要付给该职工的人民币总数
dowhilei<=7andsy>0
dowhilemz(i)*(zs(i)+1)<=sy
zs(i)=zs(i)+1
loop
sy=sy-zs(i)*mz(i)
i=i+1
loop
fori=1to7
printmz(i);":
";zs(i)
nexti
end
例2:
旅行家的预算(1999年全国青少年信息学(计算机)奥林匹克分区联赛高中组复赛试题第三题)
一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。
给定两个城市之间的距离d1、汽车油箱的容量c(以升为单位),每升汽油能行驶的距离d2、出发点每升汽油的价格p和沿途油站数n(n可以为零),油站i离出发点的距离di、每升汽油的价格pi(i=1,2,……n)。
计算结果四舍五入至小数点后两位。
如果无法到达目的地,则输出“Nosolution”。
分析:
对于任意给定的输入数据,并不一定能保证旅行家能够到达目标城市。
如果旅行家不能到达目的地,那么对最少费用的预算将毫无意义。
所以,在预算最少费用之前,有必要对输入数据进行分析,看是否能使旅行家到达目的地。
当已经确认旅行家能够到达目的地之后,再预算最少费用;如果通过对输入数据进行分析,确定旅行家不能到达目的地,那么,按照题目要求直接输出提示信息“Nosolution”即可。
算法程序如下:
(1)输入数据
(2)判断旅行家能否到达目的地
if能到达目的地then
(3)预算最少费用
(4)输出最少费用
else
输出提示信息“Nosolution”
endif
end
为了便于判断旅行家是否能够到达目的地,我们设置数组d(0ton+1),用来保存每一个油站离出发点的距离,其中,d(0)=0表示出发点,d(n+1)=d1(两个城市之间的距离)表示终点;设置变量Dmax,用来表示油箱装满汽油时汽车能够行驶的最大距离;设置标志flag,用来标识能否到达目的地,若旅行家能到达目的地,则flag=1,否则flag=0。
第
(2)步判断旅行家是否能够到达目的地,可用下面代码实现:
Dmax=c*d2
remd2为每升汽油能行驶的距离
flat=1:
i=1
whilei<=n+1andflag=1
ifd(i)-d(i-1)>Dmaxthen
flag=0
else
i=i+1
endif
wend
第(3)步预算最少费用是整个问题的核心,也是解决问题的难点。
我们可以使用贪婪算法的解题思想来进行分析。
首先仔细阅读题目,明确问题的求解目标是“最少的费用”;再联系实际进行思考,不难发现问题中隐含着一个约束条件:
汽车在到达目的地之前的每一时刻,都必须保证油箱中的汽油足够行驶到下一油站。
如果以p(i)表示第i油站的汽油价格,x(i)表示在第i油站所加汽油的量,那么,优化函数就是。
两个城市之间的距离是固定不变的,汽车从出发点到达目的地所需要的汽油总量(即)自然也是固定不变的。
根据使费用最少的求解目标,要使优化函数取得最优值(在此为最小值),必须使p(i)尽可能小:
也就是汽车要尽可能在价格便宜的油站加油。
这是解决问题的贪婪准则。
汽车每到达一个油站i(包括出发点第0站,但不包括目的地第n+1站),都要检查是否需要加油。
如果汽车在某个油站i需要加油,那么,就先将该油站的汽油价格p(i)与下一油站的汽油价格p(i+1)进行比较,若p(i)>=p(i+1),加油时,只需保证油箱中的汽油能够到达下一油站(第i+1站)即可;否则,继续将p(i)与第i+2站的汽油价格p(i+2)进行比较,……判断是否需要在第i站加油的条件可以确定为:
在到达第i站时,汽车油箱中的剩余汽油(用变量sy表示剩余汽油的多少)是否足够行驶到下一油站,即sy*d2是否大于或等于d(i+1)-d(i)。
预算最少费用的算法程序如下:
fori=0ton
①计算到达第i站时汽车油箱中还剩下的汽油量sy
if汽车需要加油then
②给汽车加油
endif
nexti
汽车在第0站(出发点)的时候,油箱中的汽油量肯定是0,不用计算;从第1站开始,以后每经过一个油站,我们都计算一下油箱中还剩下多少汽油,以便判断汽车是否需要加油。
计算的方法是:
用上一次产生的剩余汽油量减掉汽车从前面一站(第i-1站)行驶到现在的位置(第i站)所消耗的汽油量。
对以上算法中的第①步进行求精,得到下面的代码:
ifi>=1then
sy=sy-(d(i)-d(i-1))/d2
汽车到达油站i时,如果需要加油,那需要加多少油呢?
根据以上分析出来的贪婪策略:
先将油站i的汽油价格p(i)与下一油站的汽油价格p(i+1)进行比较,若p(i)>=p(i+1),只需保证油箱中的汽油能够到达下一油站即可;若p(i),则继续将p(i)与p(i+2)进行比较,……第②步给汽车加油,可以使用一个递归过程来实现。
汽车在终点站不需要加油,终点站的汽油价格对我们解题没有任何影响,为了便于程序的实现,我们规定终点站的汽油价格为0,即p(n+1)=0;另外,设置一个变量j,在调用递归过程之前给它赋初值为i+1。
第②步给汽车加油的参考程序如下:
subjy(iasinteger,jasinteger)
ifp(i)>=p(j)then
if(d(j)-d(i))/d2>cthen
//即便把油箱加满,还是不能到达j站(注意:
不是i的下一站i+1)
fy=fy+(c-sy)*p(i)
//fy用来累计费用
sy=c
else
fy=fy+((d(j)-d(i))/d2-sy)*p(i)
sy=(d(j)-d(i))/d2
endif
else
calljy(i,j+1)
endif
endsub
第十课使用贪心算法策略解决问题
作者钱晓峰来源校内资料浏览次数198
阅读权限游客身份花费金币0添加时间2008-5-10153259
贪心法是从问题的某一个初始解出发,向给定的目标推进。
推进的每一步不是依据某一个秃顶的递推式,而是作一个当时看作最佳的贪心选择,
不断的将问题实例归纳为更小的相似的子问题,并期望通过所做的局部的最优选择产生一个全局最优的解。
例1:
计算发放工资时所需的最少人民币张数。
输入数据为职工的工资数。
要求统计并输出应发给该职工面值100元、50元、20元、10元、5元、2元、1元的人民币各多少张,使总张数最少。
分析:
职工拿到的人民币总和都应等于他自己的工资数。
这是问题的约束条件。
再阅读题目,我们发现问题的求解目标是“使总张数最少”。
相应的优化函数是职工拿到的人民币张数,即。
满足约束条件并使取得最优值(在这里为最小值)的求解方案就是问题的最优解。
举个具体例子,要发给该职工638元的工资,并使总张数最少,直觉告诉我们,应先给他6张面值100元的人民币;第7张不能再给100元的,也不能给50元的,否则该职工实际拿到的工资将会超过他应得的工资;显然,第7张应为面值20元的人民币。
同理,第8张为面值10元的人民币,第9张为面值5元的人民币,第10张为面值2元的人民币,第11张为面值1元的人民币。
该职工共拿到人民币11张,分别为面值100元的6张,面值50元的没有,面值20元、10元、5元、2元和1元的各1张。
这样,不但满足了约束条件=zggz,即100×6+(20+10+5+2+1)×1=638,而且使该职工拿到的人民币张数最少。
把以上操作方法归纳出来就是一种可行的贪婪准则:
按面值从大到小的顺序分发人民币,并始终保证职工实际拿到的工资不超过他应得的工资。
参考程序如下:
初始化mz(i)分别为不同的面值;
fillchar(zs,sizeof(zs),0);
i=1
readln(sy);
while7中面值的数量还没有计算完,并还未凑满工资do
begin
1.求第i种面子的张数zs(i)
2.求当前还有多少剩余的工资要用更小的面值组成
end;
输出7中面值各几张
练习一:
发工资问题。
完成例题一中的发工资问题
(源文件:
gongzi.pas输入文件:
gongzi.in输出文件:
gongzi.out)
输入:
某职工的工资数量(小于30000的整数)
输出:
一行,7个整数分别代表10050..的张数
练习二:
排队打水问题dashui.pas
【问题描述】
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。
【输入】dashui.in
输入文件共两行,第一行为n;第二行分别表示第1个人到第n个人每人的接水时间T1,T2,…,Tn,每个数据之间有1个空格。
【输出】dashui.out
输出文件有两行,第一行为一种排队顺序,即1到n的一种排列;
第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
【样例】
water.inwater.out
1032781496105
56121991000234335599812291.90
练习三:
删数问题sanshu.pas
通过键盘输入一个高精度的正整数n(n的有效位数≤240),去掉其中任意s个数字后,剩下的数字按原左右次序将组成一个新的正整数。
编程对给定的n和s,寻找一种方案,使得剩下的数字组成的新数小。
输入:
n
s
输出:
最后剩下的最小数
【样例输入】
178543
4
【样例输出】
13
第十一课贪心算法2_经典题目与贪心解题的基本要素
作者:
钱晓峰来源:
校内资料浏览次数:
167
阅读权限:
游客身份花费金币:
0添加时间:
2008-5-1713:
26:
50
贪心算法通过一系列的选择得到问题的解,它所做的每一步选择都是当前局部最好的选择(贪心选择)。
这种方法在许多情况下能达到预期的目的。
但是并不是所有的求最优结果的问题都能用这种方式处理。
如:
在大家课本上有一个部分背包问题P122,可以用贪心算法来解决,但是如果装载的每一件物品都不能分隔,即(0-1背包问题)就不能用贪心算法来解决,(可以用回溯的方式来解决)。
有一个背包,背包容量是W(W为整数)。
有N个物品,要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
如W=150
物品
A
B
C
D
E
F
G
重量
35
30
60
50
40
10
25
价值
10
40
30
50
35
40
30
练习一部分背包问题:
物品可以任意分割,最大的价值是多少?
(源文件:
beibao1.pas输入文件:
beibao1.in输出文件:
beibao1.out)
输入输入:
第一行wn分别表示背包的容量和物品数量
第2到第n+1行每行2个数字表示每种物品的重量、价值
数据输出:
一个数保留小数点后2位最大价值
练习二0-1背包问题:
假如物品不能分割,应该带走哪几件物品。
(源文件:
beibao2.pas输入文件:
beibao2.in输出文件:
beibao2.out)
1.贪心选择性质——通过局部最优(贪心)选择来达到全局最优解
贪心策略通常是自顶向下做的。
第一步为一个贪心选择,将原问题变成一个相似的、但规模更小的问题.而后的每一步在当前看都似最佳的选择。
这种选择可能依赖于已作出的所有选择,但不依赖下一步的选择或子问题的解。
从求解的全过程来看.每—次贪心选择都将当前问题归纳为更小的相似子问题,而每一个选择都仅做一次,无重复回溯过程,因此贪心法有较高的时间效率,
删数问题中,设正整数N有P位。
第一步选择一个使剩下的p-1位数最小的数字删去,把问题纳为在p-1位正整数中去掉s-1个数字后的新数最小的子问题;第二步通过同样方法,又把问题归纳为在p-2位正整数中去掉S-2个数字后的新数最小的子问题……每次选择中.已删去的数不允许再删,而当前选择又不受将来删的数的影响。
依照上述方法不断将问题归纳为更小的互为独立的子问死直至删除s个数符为止。
显然删数问题符合贪心选择性质。
2.最优子结构——问题的最优解包含了子问题的最优解
删数问题中,N=178543,S=4。
第一步的贪心选择为删‘8’,即第一个子问题的最优解为’8’;第二步的贪心选择为删’7’,即第二个子问题(第一个子问题的子问题)的最优解为‘7’;继后的第三个,第四个于问题的最优解分别为’5’和’4’。
很显然,问题的最优解包含了四个子问题的最优解、因此删数问题具有最优子结构性质。
练习三、活动安排问题huodong.pas
[问题描述]假设有一个需要使用某一资源的n个活动组成的集合S,S={1,……,n}。
该资源一次只能被一个活动所占用,每一个活动有一个开始时间bi和结束时间ei(bi≤ei)。
若bi≥ej或者bj≥ei,则活动i和活动j兼容。
你的任务是:
选择由互相兼容的活动组成的最大集合(活动最多)。
[输入格式]
输入文件名为:
huodong.in,共n+1行,其中第1行为n,第2行到第n+1行表示n个活动的开始时间和结束时间(中间用空格隔开),格式为:
n
b1e1
………
bnen
[输出格式]
输出文件名为:
huodong.out,共两行,
第1行可举办活动的个数,
第2行为最大集合中的活动序号,每个数据之间用一个空格隔开。
按活动先后输出
[样例输入]
11
35
14
1214
812
06
811
610
57
38
59
213
[样例输出]
4
2863
练习四、防御导弹daodan.pas
问题描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。
但是这种导弹拦截系统有一个缺陷:
虽然它的第一发炮弹能够达到任意的高度,但是以后每一发炮弹都不能高于前一发的高度。
某天,雷达捕捉到敌国的导弹来袭。
由于该系统还在使用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
数据输入daodan.in
最多20个整数,分别表示导弹依次飞来的高度(雷达给出高度数据是不大于30000的正整数)
数据输出daodan.out
两个整数M和N。
表示:
这套系统最多能拦截M枚导弹,如果要拦截所有导弹最少要配备N套这种导弹系统。
输入样例
300250275252200138245
输出样例
2
最大公约数和最小公倍数
作者:
匿名来源:
网络收集浏览次数:
167
阅读权限:
游客身份花费金币:
0添加时间:
2008-5-1715:
02:
28
求两自然数,其和是667,最小公倍数与最大公约数之比是120:
1(例如(115,552)、(232,435))。
[解]两个自然数分别为m和667-m(2≤m≤333)。
处理对象:
m(自然数)、l(两数的最小公倍数)、g(两数的最大公约数)。
处理步骤:
对m从2到333检查l与g的商为120,且余数为0时,打印m与667-m。
第一层抽象程序:
ProgramTwoNum;
Varm,l,g:
integer;
Beginform:
=2to333do
beginl:
=lcm(m,667-m);{求最小公倍数}
g:
=gcd(m,667-m);{求最大公约数}
if(l=g*120)and(lmodg=0)then
writeln(m:
5,667-m:
5);
end;
End.
第二层考虑函数lcm(最小公倍数)、gcd(最大公约数)的细化。
最大公约数问题是对参数a、b,找到一个数i能整除a与b,i就是gcd的函数值。
Functiongcd(a,b:
integer):
integer;
vari:
integer;
beginfori:
=adownto1do
ifnot((amodi=0)or(bmodi=0))thengcd:
=i;
end;
而最小公倍数的计算是:
若干个b之和,若能被a整除,则该和便是a、b的最小公倍数。
Functionlcm(a,b:
integer):
integer;
vari:
integer;
begini:
=b;
whileimoda=0doi:
=i+b;
lcm:
=i;
end;
贪心算法
一、算法思想
贪心法的基本思路:
——从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。
当达到某算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:
1.不能保证求得的最后解是最佳的;
2.不能用来求最大或最小解问题;
3.只能求满足某些约束条件的可行解的范围。
实现该算法的过程:
从问题的某一初始解出发;
while能朝给定总目标前进一步do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
二、例题分析
1、[背包问题]有一个背包,背包容量是M=150。
有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品
A
B
C
D
E
F
G
重量
35
30
60
50
40
10
25
价值
10
40
30
50
35
40
30
分析:
目标函数:
∑pi最大
约束条件是装入的物品总重量不超过背包容量:
∑wi<=M(M=150)
(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
(2)每次挑选所占空间最小的物品装入是否能得到最优解?
(3)每次选取单位容量价值最大的物品,成为解本题的策略。
源程序
2、[单源最短路径]一个有向图G,它的每条边都有一个非负的权值c[i,j],“路径长度”就是所经过的所有边的权值之和。
对于源点需要找出从源点出发到达其他所有结点的最短路径。
E.Dijkstra发明的贪婪算法可以解决最短路径问题。
算法的主要思想是:
分步求出最短路径,每一步产生一个到达新目的顶点的最短路径。
下一步所能达到的目的顶点通过如下贪婪准则选取:
在未产生最短路径的顶点中,选择路径最短的目的顶点。
设置顶点集合S并不断作贪心选择来扩充这个集合。
当且仅当顶点到该顶点的最短路径已知时该顶点属于集合S。
初始时S中只含源。
设u为G中一顶点,我们把从源点到u且中间仅经过集合S中的顶点的路称为从源到u特殊路径,并把这个特殊路径记录下来(例如程序中的dist[i,j])。
每次从V-S选出具有最短特殊路径长度的顶点u,将u添加到S中,同时对特殊路径长度进行必要的修改。
一旦V=S,就得到从源到其他所有顶点的最短路径,也就得到问题的解。
Dijkstra.pas
3、[机器调度]现有N项任务和无限多台机器。
任务可以在机器上处理。
每件任务开始时间和完成时间有下表:
任务
a
b
c
d
e
f
g
开始(si)
0
3
4
9
7
1
6
完成(fi)
2
7
7
11
10
5
8
在可行分配中每台机器在