贪心算法详解 联系客服

发布时间 : 星期五 文章贪心算法详解更新完毕开始阅读8e53bfca5a8102d277a22f2b

Begin

S:=S+M*(P[I]/W[I]); Break; End; End;

Procedure Out; {输出} Begin

Assign(Output,Fout); Rewrite(Output); Writeln(S:0:0); Close(Output); End;

Begin {主程序} Init; Work; Out; End.

因此,利用贪心策略解题,需要解决两个问题:

首先,确定问题是否能用贪心策略求解;一般来说,适用于贪心策略求解的问题具有以下特点:

① 可通过局部的贪心选择来达到问题的全局最优解。运用贪心策略解题,一般来说需要一步步的进行多次的贪心选择。在经过一次贪心选择之后,原问题将变成一个相似的,但规模更小的问题,而后的每一步都是当前看似最佳的选择,且每一个选择都仅做一次。

② 原问题的最优解包含子问题的最优解,即问题具有最优子结构的性质。在背包问题中,第一次选择单位质量最大的货物,它是第一个子问题的最优解,第二次选择剩下的货物中单位重量价值最大的货物,同样是第二个子问题的最优解,依次类推。 其次,如何选择一个贪心标准?正确的贪心标准可以得到问题的最优解,在确定采用贪心策略解决问题时,不能随意的判断贪心标准是否正确,尤其不要被表面上看似正确的贪心标准所迷惑。在得出贪心标准之后应给予严格的数学证明。

下面来看看0-1背包问题。

给定一个最大载重量为M的卡车和N种动物。已知第i种动物的重量为Wi,其最大价值为Vi,设定M,Wi,Vi均为整数,编程确定一个装货方案,使得装入卡车中的所有动物总价值最大。

分析:对于N种动物,要么被装,要么不装,也就是说在满足卡车载重的条件下,如何选择动物,使得动物价值最大的问题。

即确定一组X1,X2,?,Xn, Xi∈{0,1}

f(x)=max(∑Xi*Vi) 其中,∑(Xi*Wi)≦W

从直观上来看,我们可以按照上例一样选择那些价值大,而重量轻的动物。也就是可以按价值质量比(Vi/Wi)的大小来进行选择。可以看出,每做一次选择,都是从剩下的动物中选择那些Vi/Wi最大的,这种局部最优的选择是否能满足全局最优呢?我们来看看一个简单的例子:

设N=3,卡车最大载重量是100,三种动物A、B、C的重量分别是40,50,70,其对应

的总价值分别是80、100、150。

情况A:按照上述思路,三种动物的Vi/Wi分别为2,2,2.14。显然,我们首先选择动物C,得到价值150,然后任意选择A或B,由于卡车最大载重为100,因此卡车不能装载其他动物。

情况B:不按上述约束条件,直接选择A和B。可以得到价值80+100=180,卡车装载的重量为40+50=90。没有超过卡车的实际载重,因此也是一种可行解,显然,这种解比上一种解要优化。

问题出现在什么地方呢?我们看看图2-18

图23 卡车装载货物情况分析

从图23中明显可以看出,情况A,卡车的空载率比情况B高。也就是说,上面的分析,只考虑了货物的价值质量比,而没有考虑到卡车的运营效率,因此,局部的最优化,不能导致全局的最优化。

因此,贪心不能简单进行,而需要全面的考虑,最后得到证明。

例26排队打水问题

有N个人排队到R个水龙头去打水,他们装满水桶的时间为T1,T2,?,Tn为整数且各不相等,应如何安排他们的打水顺序才能使他们花费的时间最少? 分析:由于排队时,越靠前面的计算的次数越多,显然越小的排在越前面得出的结果越小(可以用数学方法简单证明,这里就不再赘述),所以这道题可以用贪心法解答,基本步骤:

(1) 将输入的时间按从小到大排序;

(2) 将排序后的时间按顺序依次放入每个水龙头的队列中; (3) 统计,输出答案。

参考程序:

Program Exam26;

Const Finp='Input.Txt'; Fout='Output.Txt';

Var A :Array[1..100] Of Integer; S :Array[1..100] Of Longint; N,M :Integer; Min :Longint; Procedure Init; {读入数据}

Var I :Integer; Begin

Assign(Input,Finp); Reset(Input);

Readln(N,M);

For I:=1 To N Do Read(A[I]); Close(Input); End;

Procedure Sort(L,R:Integer); {将时间从小到大排序} Var I,J,X,Y :Integer; Begin

I:=L; J:=R; X:=A[(L+R) Div 2]; Repeat

While (A[I]<=X)And(I=X)And(J>L) Do Dec(J); If I<=J Then Begin

Y:=A[I]; A[I]:=A[J]; A[J]:=Y; Inc(I); Dec(J); End; Until I>J;

If LI Then Sort(I,R); End;

Procedure Work;

Var I,J,K :Integer; Begin

Fillchar(S,Sizeof(S),0); J:=0; Min:=0;

For I:=1 To N Do {用贪心法求解} Begin Inc(J);

If J=M+1 Then J:=1; S[J]:=S[J]+A[I]; Min:=Min+S[J]; End;

Assign(Output,Fout); Rewrite(Output); { Writeln(Min); Close(Output); End;

Begin {主程序} Init;

Sort(1,N); Work; End.

输出解答} 均分纸牌

有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。

移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。 例如 N=4,4 堆纸牌数分别为: ① 9 ② 8 ③ 17 ④ 6 移动3次可达到目的:

从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。 输入格式

N(N 堆纸牌,1 <= N <= 100)

A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000 输出格式 Output Format

所有堆均达到相等时的最少移动次数。 样例输入 4 9 8 17 6 样例输出 3 题解:

program zzh; var

a:array[1..99]of integer; b,c,d,e,f,g,n,i:integer; begin

read(n); c:=0;

for b:=1 to n do read(a[b]);

for b:=1 to n do c:=c+a[b]; d:=c div n;

if c mod n=0 then begin