贪心算法2

本文介绍了贪心算法的概念及其在解决均分纸牌问题中的应用。通过一个具体的实例,展示了如何使用贪心算法在有限移动次数下使每堆纸牌数量相等。虽然贪心算法的选择仅基于局部最优解,但在某些问题中如本例,它能得出全局最优解。文章还讨论了贪心算法的适用性和确定贪心标准的重要性,并提供了错误贪心标准的反例,以强调正确选择贪心标准对于确保最优解的关键作用。

在求最优解问题的过程中 ,依据某种贪心标准,从问题的初始状态出发,直接去求解每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是谈心算法。

从贪心算法的定义可以看出,贪心法并不是从整体上考虑问题,它所做出的选择只是在某中意义上的局部最优解,而由问题自身的特性决定了该题运用贪心算法可以得到最优解。

例题1:均分纸牌(NOIP2002tg

问题描述:有N堆纸牌,编号分别为12n。每堆上有若干张,但纸牌总数必为n的倍数.可以在任一堆上取若干张纸牌,然后移动。移牌的规则为:在编号为1上取的纸牌,只能移到编号为2的堆上;在编号为n的堆上取的纸牌,只能移到编号为n-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。例如:n=44堆纸牌分别为:① 9 ② 8 ③ 17 ④ 6 移动三次可以达到目的:从③取4张牌放到④ 再从③区3张放到②然后从②去1张放到①。

输入:键盘输入文件名。

文件格式:n (n堆纸牌 1<=n<=100)

a1,a2,a3,… an(n堆纸牌,每堆纸牌初始数,1<=ai<=10000)

输出:输出至屏幕。格式为:所有堆数均达到相等时的最少移动次数。

输入输出样例:a.in:     4

9         8 17 6

屏幕显示:3

算法分析:设a[i]为第I堆纸牌的张数(0<=I<=n),v为均分后每堆纸牌的张数,s为最小移动次数。

我们用贪心法,按照从左到右的顺序移动纸牌。如第I堆的纸牌数不等于平均值,则移动一次(即s加1),分两种情况移动:

1.若a[i]>v,则将a[i]-v张从第I堆移动到第I+1堆;

2.若a[i]<v,则将v- a[i]张从第I+1堆移动到第I堆。

为了设计的方便,我们把这两种情况统一看作是将a[i]-v从第I堆移动到第I+1堆,移动后有a[i]=v; a[I+1]=a[I+1]+a[i]-v.

在从第I+1堆取出纸牌补充第I堆的过程中可能回出现第I+1堆的纸牌小于零的情况。

如n=3,三堆指派数为1 2 27 ,这时v=10,为了使第一堆为10,要从第二堆移9张到第一堆,而第二堆只有2张可以移,这是不是意味着刚才使用贪心法是错误的呢?

我们继续按规则分析移牌过程,从第二堆移出9张到第一堆后,第一堆有10张,第二堆剩下-7张,在从第三堆移动17张到第二堆,刚好三堆纸牌都是10,最后结果是对的,我们在移动过程中,只是改变了移动的顺序,而移动次数不便,因此此题使用贪心法可行的。

源程序:

var I,n,s:integer; v:longint;   a:array[1..100] of longint;

f:text; fil:string;

begin   realn(fil);

        assign(f,fi); reset(f);

        readln(f,n); v:=0;

        for I:=1 to n do begin

read(f,a[i]), inc(v,a[i]);

                         end;

        v:=v div n;   {每堆牌的平均数}

        for I:=1 to n-1 do

          if a[i]<>v then   {贪心选择}

                        begin   inc(s);   {移牌步数记数}

                              a[I+1]=a[I+1]+a[i]-v;{使第I堆牌数为v}

                         end;

          writeln(s);

end.

    利用贪心法解题,需要解决两个问题:

    一是问题是否适合用贪心法求解。我们看一个找币的例子,如果一个货币系统有三种币值,面值分别为一角、五分和一分,求最小找币数时,可以用贪心法求解;如果将这三种币值改为一角一分、五分和一分,就不能使用贪心法求解。用贪心法解题很方便,但它的适用范围很小,判断一个问题是否适合用贪心法求解,目前还没有一个通用的方法,在信息学竞赛中,需要凭个人的经验来判断何时该使用贪心算法。

    二是确定了可以用贪心算法之后,如何选择一个贪心标准,才能保证得到问题的最优解。在选择贪心标准时,我们要对所选的贪心标准进行验证才能使用,不要被表面上看似正确的贪心标准所迷惑,如下面的例子。

例题2:设有n个正整数,将它们连接成一排,组成一个最大的多位整数。

例如:n=3时,3个整数13,312,343,连成的最大整数为34331213。

又如:n=4时,4个整数7,13,4,246,连成的最大整数为7424613。

输入:n

      N个数

输出:连成的多位数

算法分析:此题很容易想到使用贪心法,在考试时有很多同学把整数按从大到小的顺序连接起来,测试题目的例子也都符合,但最后测试的结果却不全对。按这种标准,我们很容易找到反例:12,121应该组成12121而非12112,那么是不是相互包含的时候就从小到大呢?也不一定,如12,123就是12312而非12123,这种情况就有很多种了。是不是此题不能用贪心法呢?

其实此题可以用贪心法来求解,只是刚才的标准不对,正确的标准是:先把整数转换成字符串,然后在比较a+b和b+a,如果a+b>b+a,就把a排在b的前面,反之则把a排在b的后面。

源程序:

Var s:array[1..20] of string;

     T:string;I,k,j,n:longint;

Begin

     Readln(n);

     For i:=1 to n do

        Begin   read(k);

                Str(k,s[i]);

         End;

     For i:=1 to n-1 do

         For j:=i+1 to n do

           If s[i]+s[j]<s[j]+s[i] then

              Begin   t:=s[i];

                      S[i]:=s[j];

                      S[j]:=t;

              End;

      For i:=1 to n do write(s[i]);

End.

贪心算法所作的选择可以依赖于以往所作过的选择,但决不依赖于将来的选择,也不依赖于子问题的解,因此贪心算法与其他算法相比具有一定的速度优势。如果一个问题可以同时用几种方法解决,谈心算法应该是最好的选择之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值