贪心算法——>解决埃及分数、数列极差问题

一、算法的描述

从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能地求得最好的解。当达到某算法中的某一步不需要再继续前进时,算法停止。

二、算法的设计思想

首先贪婪算法的原理是通过局部最优来达到全局最优,采用的是逐步构造最优解的方法。在每个阶段,都作出一个看上去最优的(在一定的标准下),决策一旦作出,就不可再更改。用贪婪算法只能解决通过局部最优的策略能达到全局最优的问题。因此一定要注意判断问题是否适合采用贪婪算法策略,找到的解是否一定是问题的最优解。

三、算法的适用场景

贪婪算法对问题只需考虑当前局部信息就要做出决策,也就是说使用贪婪算法的前提是“局部最优策略能导致产生全局最优解”。该算法的适用范围较小,
若应用不当,不能保证求得问题的最佳解。一般情况下通过一些实际的数据例子(当然要有一定的普遍性),就能从直观上就能判断一个问题是否可以用贪婪算法,如数列极差问题。更准确的方法是通过数学方法证明问题对贪婪策略的选用性。

四、算法框架

从问题的某一初始解出发;
           while能朝给定总目标前进一步do;
                    利用可行的决策,求出可行解的一个解元素;
         由所有解元素组合成问题的一个可行解。

五、典型例题:

1.埃及分数

题目:
设计一个算法,把一个真分数表示为埃及分数之和的形式。所谓埃及分数,是指分子为1的分数。如:7/8=1/2+1/3+1/24。
实例:
逐步选择分数所包含的最大埃及分数,这些埃及分数之和就是问题的一个解。
如:
7/8>1/2,
7/8-1/2>1/3,
7/8-1/2-1/3=1/24。
思路分析:
1)找最小的n(最大的埃及分数1/n),使分数f>1/n;
2)输出1/n;
3)计算f=f-1/n;
4)若此时的f是埃及分数,输出f,算法结束,否则返回1)。
5)记真分数F=A/B;对B/A进行整除运算,商为D,余数为0<K<A,它们导出关系如下:
B=AD+K,B/A=D+K/A<D+1,A/B>1/(D+1),记C=D+1。
6)这样分数F所包含的“最大”埃及分数就是1/C。
进一步计算:A/B-1/C=(A
C-B)/BC
7)也就是说继续要解决的是有关分子为A=A
C-B,分母为B=BC的问题。
实例:
7/8=1/2+1/3+1/24的解题步骤:
同样用变量A表示分子,变量B表示分母;
C=8/7+1=2 //说明7/8>1/2,
打印1/2
A=72-8=6,B=BC=16 //计算7/8-1/2=(72-8)/(72)=6/16=A/B
C=16/6+1=3 //说明16/6>1/3,
打印1/3
A=63-16=2,B=BC=16
3=48
//计算6/16-1/3=(63-16)/(163)=2/48=A/B
A>1但B/A为整数24,打印1/24 结束。

main( )
{  
    int a,b,c;
    print(“input   element”);
    input(a);
    print(“input   denominator”);
    input(b);
    if(a>=b)
         print(“input  error”);
    else if (a=1 or b mod a=0)  
               print( a, "/",b, "=" 1, "/",b/a);
         else
               while(a<>1)
                { 
                   c = b /a + 1;
                   a = a * c – b; b = b * c;
                   print( "1/",c);
                   if( a > 1)
                        print("+");
                   if  (b mod  a =0) 
                   { 
                       print ("1/"; b / a);
                        a=1; 
                   } 
               }    
 }

2.数列极差问题

题目
在黑板上写N个正整数排成一个数列,进行如下操作:每一次擦去其中的两个数a和b,然后在数列中加入一个数a×b+1,如此下去直至黑板上剩下一个数,在所有按这种操作方式最后得到的数中,最大的记作max,最小的记作min,则该数列的极差定义为M=max-min。
实例:
对三个具体数据3,5,7讨论,可能有以下三种结果:
(35+1)7+1=113,
(3
7+1)5+1=111,
(5
7+1)3+1=109
(先运算小数据得到的是最大值,先运算大数据得到的是最小值。)
思路分析:
(a
b+1)c+1=aa
a+(2k1+k2)aa+(k1(k1+k2)+1)a+k1+k2+1
(a
c+1)b+1=aa
a+(2k1+k2)aa+(k1(k1+k2)+1)a+k1+1
(b
c+1)a+1=aa
a+(2k1+k2)a*a+(k1(k1+k2)+1)*a+1
显然此问题适合用贪婪策略,不过在求最大值时,要先选择较小的数操作。求最小值时,要先选择较大的数操作。这是一道两次运用贪心策略解决的问题。
1)不断从现有的数据中,选取最大和最小的两个数,计算后的结果继续参与运算,直到剩余一个数算法结束。
2)选取最大和最小的两个数较高效的算法是用二分法完成,这里仅用简单的逐个比较方法来求解。注意到由于找到的两个数将不再参与其后的运算,其中一个用它们的计算结果代替,另一个用当前的最后一个数据覆盖即可。所以不但要选取最大和最小,还必须记录它们的位置,以便将其覆盖。
3)求max、min过程必须独立,即求max和min都必须从原始数据开始,否则不能找到真正的max和min。

int s1,s2;
 main( )
{   int j,n,a[100],b[100],max,min;
     print(“How mang data?);   
     input(n);
     print(“input these data”);
     for (j=1;j<=n;j=j+1)
     {
            input(a[j]);    
            b[j]=a[j];}
            min= calculatemin(a,n);
            max= calculatemax(b,n);
            print(“max-min=, max-min)}
}
calculatemin(int a[],int n)
 {   
    int j;
    while (n>2)
    {      
        max2(a,n);    
        a[s1]= a[s1]* a[s2]+1;
        a[s2]=a[n];    
        n=n-1;    
    }
     return(a[1]* a[2]+1);
}
max2(int a[],int n)
{    
    int  j;
    if(a[1]>=a[2])       
    {  
        s1=1;  
        s2=2;
    }//大->s1
    else  
    {   
        s1=2;  
        s2=1;
    } 
    for (j=3;j<=n;j++)
    {        
        if (a[j]>a[s1])         
        {   
            s2=s1;   
            s1=j;
        }
        else  if (a[j]>a[s2])        
                    s2=j;         
    }
} 
calculatemax(int a[],int n)
{  
    int j;
    while (n>2)
    {  
        min2(a,n);         
        a[s1]= a[s1]* a[s2]+1;
        a[s2]=a[n];         
        n=n-1;
    }
     return(a[1]* a[2]+1);
}  
min2(int a[ ],int n)
{  
    int  j;
    if(a[1]<=a[2])           
    {   
        s1=1;  
        s2=2;
    }
    else             
    {   
        s1=2;  
        s2=1;
    }
    for (j=3;j<=n;j++)
           if (a[j]<a[s1])       
           {    
               s2=s1;  
               s1=j;
           }
           else  if (a[j]<a[s2])          
                        s2=j;
}
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值