贪心算法的应用_第1页
贪心算法的应用_第2页
贪心算法的应用_第3页
贪心算法的应用_第4页
贪心算法的应用_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。从贪心算法的定义可以看出,贪心法并不是从整体上考虑问题,它所做出的选择只是在某种意义上的局部最优解,而由问题自身的特性决定了该题运用贪心算法可以得到最优解。我们看看下面的例子例1 均分纸牌(NOIP2002tg)问题描述有 N 堆纸牌,编号分别为 1,2,, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的

2、纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。例如 N=4,4 堆纸牌数分别为:98176移动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)输 出:输出至屏幕。格式为:所有堆均达到

3、相等时的最少移动次数。输入输出样例a.in:49 8 17 6屏慕显示:3算法分析:设ai为第i堆纸牌的张数(0<=i<=n),v为均分后每堆纸牌的张数,s为最小移到次数。我们用贪心法,按照从左到右的顺序移动纸牌。如第i堆(0<i<n)的纸牌数ai不等于平均值,则移动一次(即s加1),分两种情况移动:(1)    若ai>v,则将ai-v张纸牌从第I堆移动到第I+1堆;(2)    若ai<v,则将v -ai张纸牌从第I+1堆移动到第I堆;为了设计的方便,我们把这两种情况统一看作是将aI-v张牌从第

4、I堆移动到第I+1堆;移动后有:aI:=v;aI+1:=aI+1+aI-v;在从第i+1堆中取出纸牌补充第i堆的过程中,可能会出现第i+1堆的纸牌数小于零(ai+1+ai-v<0 )的情况。如n=3,三堆纸牌数为(1,2,27)这时v=10,为了使第一堆数为10,要从第二堆移9张纸牌到第一堆,而第二堆只有2张纸牌可移,这是不是意味着刚才使用的贪心法是错误的呢?我们继续按规则分析移牌过程,从第二堆移出9张到第一堆后,第一堆有10张纸牌,第二堆剩下-7张纸牌,再从第三堆移动17张到第二堆,刚好三堆纸牌数都是10,最后结果是对的,从第二堆移出的牌都可以从第三堆得到。我们在移动过程中,只是改变了

5、移动的顺序,而移动的次数不变,因此此题使用贪心法是可行的。 源程序:var i,n,s:integer;v:longint;  a:array1.100of longint;  f:text;fil:string;begin  readln(fil);assign(f,fil);reset(f);  readln(f,n);v:=0;  for i:=1 to n do begin    read(f,ai); inc(v,ai);  end;  v:=v div n; 每堆牌的平均数 

6、; for i:=1 to n-1 do if ai<>v then 贪心选择begin      inc(s);移牌步数计数      ai+1:=ai+1+ai-v;使第i堆牌数为v    end;then  writeln(s);end.利用贪心算法解题,需要解决两个问题:一是问题是否适合用贪心法求解。我们看一个找币的例子,如果一个货币系统有3种币值,面值分别为一角、五分和一分,求最小找币数时,可以用贪心法求解;如果将这三种币值改为一角一

7、分、五分和一分,就不能使用贪心法求解。用贪心法解题很方便,但它的适用范围很小,判断一个问题是否适合用贪心法求解,目前还没有一个通用的方法,在信息学竞赛中,需要凭个人的经验来判断何时该使用贪心算法。二是确定了可以用贪心算法之后,如何选择一个贪心标准,才能保证得到问题的最优解。在选择贪心标准时,我们要对所选的贪心标准进行验证才能使用,不要被表面上看似正确的贪心标准所迷惑,如下面的列子。例2 (NOIP1998tg)设有n个正整数,将他们连接成一排,组成一个最大的多位整数。例如:n=3时,3个整数13,312,343,连成的最大整数为:34331213又如:n=4时,4个整数7,13,4,246连接

8、成的最大整数为7424613输入:N N个数输出:连接成的多位数算法分析:此题很容易想到使用贪心法,在考试时有很多同学把整数按从大到小的顺序连接起来,测试题目的例子也都符合,但最后测试的结果却不全对。按这种贪心标准,我们很容易找到反例:12,121 应该组成12121而非12112,那么是不是相互包含的时候就从小到大呢?也不一定,如:12,123 就是12312而非12112,这样情况就有很多种了。是不是此题不能用贪心法呢?其实此题是可以用贪心法来求解,只是刚才的贪心标准不对,正确的贪心标准是:先把整数化成字符串,然后再比较a+b和b+a,如果a+b>b+a,就把a排在b的前面,反之则把

9、a排在b的后面。源程序:var  s:array1.20 of string;  t:string;i,j,k,n:longint;begin  readln(n);  for i:=1 to n do begin    read(k);    str(k,si);  end;  for i:=1 to n-1 do    for j:=i+1 to n do      if si+sj<s

10、j+si then      begin交换        t:=si;        si:=sj;        sj:=t;      end;  for i:=1 to n do write(si);end.贪心算法所作的选择可以依赖于以往所作过的选择,但决不依赖于将来的

11、选择,也不依赖于子问题的解,因此贪心算法与其它算法相比具有一定的速度优势。如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。 贪心算法经典例子(2009-07-15 10:17:04)标签:贪心 算法 背包问题 it  分类:简单算法一、定义什么是贪心算法呢?所谓贪心算法是指,在对问题求解时,总是做出在当前看来最好的选择。也就是说,不从整体最优解出发来考虑,它所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题都能产生整体最优解或整体最优解的近似解。贪心算法的基本思路如下:1

12、.建立数学模型来描述问题。2.把求解的问题分成若干个子问题。3.对每个子问题求解,得到每个子问题的局部最优解。4.把每个子问题的局部最优解合成为原来问题的一个解。实现该算法的过程:从问题的某一初始状态出发;while 能朝给定总目标前进一步 do求出可行解的一个解元素;由所有解元素组合成问题的一个可行解;二、例题分析背包问题有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。物品 A  B  C  D  E  F  G重量 35 30 60 50 4

13、0 10 25价值 10 40 30 50 35 40 30记得当时学算法的时候,就是这个例子,可以说很经典。分析:目标函数: pi最大约束条件是装入的物品总重量不超过背包容量,即wi<=M( M=150)(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?(2)每次挑选所占重量最小的物品装入是否能得到最优解?(3)每次选取单位重量价值最大的物品,成为解本题的策略?贪心算法是很常见的算法之一,这是由于它简单易行,构造贪心策略简单。但是,它需要证明后才能真正运用到题目的算法中。一般来说,贪心算法的证明围绕着整个问题的最优解一定由在贪心策略中存在的子问题的最优解得来的。

14、对于本例题中的3种贪心策略,都无法成立,即无法被证明,解释如下:(1)贪心策略:选取价值最大者。反例:W=30物品:A  B  C重量:28 12 12价值:30 20 20根据策略,首先选取物品A,接下来就无法再选取了,可是,选取B、C则更好。(2)贪心策略:选取重量最小。它的反例与第一种策略的反例差不多。(3)贪心策略:选取单位重量价值最大的物品。反例:W=30物品:A  B  C重量:28 20 10价值:28 20 10根据策略,三种物品单位重量价值一样,程序无法依据现有策略作出判断,如果选择A,则答案错误。值得注意的是,贪心算法并不是

15、完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。比如,求最小生成树的Prim算法和Kruskal算法都是漂亮的贪心算法。均分纸牌有N堆纸牌,编号分别为1,2,n。每堆上有若干张,但纸牌总数必为n的倍数.可以在任一堆上取若干张纸牌,然后移动。移牌的规则为:在编号为1上取的纸牌,只能移到编号为2的堆上;在编号为n的堆上取的纸牌,只能移到编号为n-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。例如:n=4,4堆纸牌分别为: 9 8 17 6 移动三次可以达到目的:从取4张牌放到 再从区3张放到然后从去1张

16、放到。输入输出样例:49 8 17 6屏幕显示:3算法分析:设ai为第I堆纸牌的张数(0<=I<=n),v为均分后每堆纸牌的张数,s为最小移动次数。我们用贪心算法,按照从左到右的顺序移动纸牌。如第I堆的纸牌数不等于平均值,则移动一次(即s加1),分两种情况移动:1若ai>v,则将ai-v张从第I堆移动到第I+1堆;2若ai<v,则将v-ai张从第I+1堆移动到第I堆。为了设计的方便,我们把这两种情况统一看作是将ai-v从第I堆移动到第I+1堆,移动后有ai=v; aI+1=aI+1+ai-v.在从第I+1堆取出纸牌补充第I堆的过程中可能回出现第I+1堆的纸牌小

17、于零的情况。如n=3,三堆指派数为1 2 27 ,这时v=10,为了使第一堆为10,要从第二堆移9张到第一堆,而第二堆只有2张可以移,这是不是意味着刚才使用贪心法是错误的呢?我们继续按规则分析移牌过程,从第二堆移出9张到第一堆后,第一堆有10张,第二堆剩下-7张,在从第三堆移动17张到第二堆,刚好三堆纸牌都是10,最后结果是对的,我们在移动过程中,只是改变了移动的顺序,而移动次数不便,因此此题使用贪心法可行的。Java源程序:public class Greedy     public static void main(String args)  

18、60;int n = 0, avg =0, s = 0;  Scanner scanner = new Scanner(System.in);  ArrayList<Integer> array = new ArrayList<Integer>();  System.out.println("Please input the number of heaps:");  n = scanner.nextInt();  System.out.println(

19、"Please input heap number:");  for (int i = 0; i < n; i+)    array.add(scanner.nextInt();    for(int i = 0; i < array.size(); i +)   avg += array.get(i);    avg = avg/array.size();  System.out.p

20、rintln(array.size();  System.out.println(avg);  for(int i = 0; i < array.size()-1; i +)   s+;   array.set(i+1, array.get(i+1)+array.get(i)-avg);       System.out.println("s:" + s); 利用贪心算法解题,需要解决两个问

21、题:一是问题是否适合用贪心法求解。我们看一个找币的例子,如果一个货币系统有三种币值,面值分别为一角、五分和一分,求最小找币数时,可以用贪心法求解;如果将这三种币值改为一角一分、五分和一分,就不能使用贪心法求解。用贪心法解题很方便,但它的适用范围很小,判断一个问题是否适合用贪心法求解,目前还没有一个通用的方法,在信息学竞赛中,需要凭个人的经验来判断。二是确定了可以用贪心算法之后,如何选择一个贪心标准,才能保证得到问题的最优解。在选择贪心标准时,我们要对所选的贪心标准进行验证才能使用,不要被表面上看似正确的贪心标准所迷惑,如下面的例子。最大整数设有n个正整数,将它们连接成一排,组成一个最大的多位整

22、数。例如:n=3时,3个整数13,312,343,连成的最大整数为34331213。又如:n=4时,4个整数7,13,4,246,连成的最大整数为7424613。输入:nN个数输出:连成的多位数算法分析:此题很容易想到使用贪心法,在考试时有很多同学把整数按从大到小的顺序连接起来,测试题目的例子也都符合,但最后测试的结果却不全对。按这种标准,我们很容易找到反例:12,121应该组成12121而非12112,那么是不是相互包含的时候就从小到大呢?也不一定,如12,123就是12312而非12123,这种情况就有很多种了。是不是此题不能用贪心法呢?其实此题可以用贪心法来求解,只是刚才的标准不对,正确

23、的标准是:先把整数转换成字符串,然后在比较a+b和b+a,如果a+b>=b+a,就把a排在b的前面,反之则把a排在b的后面。java源程序:public static void main(String args)  String str = ""  ArrayList<String> array = new ArrayList<String>();  Scanner in = new Scanner(System.in);  System.out.println(&

24、quot;Please input the number of data:");  int n = in.nextInt();  System.out.println("Please input the data:");  while (n- > 0)    array.add(in.next();       for(int i = 0; i < array.size(); i +) 

25、;  for(int j = i + 1; j < array.size(); j +)    if(array.get(i) + array.get(j).compareTo(array.get(j) + array.get(i) < 0)     String temp = array.get(i);     array.set(i, array.get(j);     

26、array.set(j, temp);           for(int i = 0; i < array.size(); i +)   str += array.get(i);    System.out.println("str=:"+str);   贪心算法所作的选择可以依赖于以往所作过的选择,但决不依赖于将来的选择,也不依赖于子问题的解,因此贪心算法与

27、其他算法相比具有一定的速度优势。如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。贪心算法实例:找零钱(Java实现) 收藏 /*从桌面txt文件里读取零钱数目以及需要找的钱总额然后利用贪心算法求解之后重新生成txt文件*import java.io.*;public class GreedySelect public GreedySelect( String file)throws IOException try this.file=file; FileReader reader=new FileReader(file); BufferedReader Breader=new

28、 BufferedReader(reader); String s=Breader.readLine(); int i,j; change=new Change10; int k=0; while(s!=null) j=0; i=0; String temp=new String9; temp=s.split("s"); changek=new Change(); while(i<6) int num=Integer.parseInt(tempi); if(num=0) j+; changek.coinsi=num; i+; if(j=5) break; double

29、 money=Double.parseDouble(tempi)*100; changek.money=(int)money; k+; s=Breader.readLine(); client=k-1; Breader.close(); catch(IOException e ) e.printStackTrace(); public void greedySort()/核心代码,贪心求解 double money; double temp=0; for(int i=0;i<client;i+) System.out.println(changei); money=changei.mon

30、ey; for(int j=5;j>=0;j-) if(j=5) temp=200; else if(j=4) temp=100; else if(j=3) temp=50; else if(j=2) temp=20; else if(j=1) temp=10; else if(j=0) temp=5; if(money>=temp&&changei.coinsj>0) money-=temp; changei.needCoinsj+; changei.coinsj-; if(money>=temp&&changei.coinsj>

31、0) j+; if(money!=0) changei.successful=false; else changei.successful=true; public void saveResult()throws IOException/保存解决方案文件到桌面 try FileWriter fileW=new FileWriter("C:Documents and SettingsAdministrator桌面 solution.txt"); PrintWriter writer=new PrintWriter(fileW); for(int i=0;i<client

32、;i+) if(changei.successful) writer.println("顾客 "+i+" 需要 "+changei.money+"找零钱如下:"); for(int j=0;j<6;j+) int k=changei.needCoinsj; if(k>0) if(j=0) writer.println("五分: "+k); else if(j=1) writer.println("一角: "+k); else if(j=2) writer.println("

33、两角: "+k); else if(j=3) writer.println("五角: "+k); else if(j=4) writer.println("一元:"+k); else if(j=5) writer.println("一元:"+k); else writer.println("顾客 "+i+"无法完成"); writer.close(); catch(IOException e) e.printStackTrace(); private int client; priva

34、te Change change; private String file; private class Change public Change()/定义顾客问题类 coins=new int6; needCoins=new int6; successful=false; money=0; public int coins; public int needCoins; public int money; public boolean successful; (一) 问题描述    一辆汽车加满油后可以行驶N千米。旅途中有若干个加油站。指出若要使沿途的加油次数最少

35、,设计一个有效的算法,指出应在那些加油站停靠加油。    给出N,并以数组的形式给出加油站的个数及相邻距离,指出若要使沿途的加油次数最少,设计一个有效的算法,指出应在那些加油站停靠加油。要求:算法执行的速度越快越好。    (二) 问题分析(前提行驶前车里加满油)    对于这个问题我们有以下几种情况:设加油次数为k,每个加油站间距离为ai;i=0,1,2,3n    1.始点到终点的距离小于,则加油次数k=0;    2.始点到终点的距离大于N

36、,    A  加油站间的距离相等,即i=aj=L=N,则加油次数最少k=n;    B  加油站间的距离相等,即i=aj=L>N,则不可能到达终点;    C  加油站间的距离相等,即i=aj=L<N,则加油次数k=n/N(n%N=0)或k=n/N+1(n%N!=0);    D  加油站间的距离不相等,即i!=aj,则加油次数k通过以下算法求解。    (三)算法描述  

37、  贪心算法的基本思想    该题目求加油最少次数,即求最优解的问题,可分成几个步骤,一般来说,每个步骤的最优解不一定是整个问题的最优解,然而对于有些问题,局部贪心可以得到全局的最优解。贪心算法将问题的求解过程看作是一系列选择,从问题的某一个初始解出发,向给定目标推进。推进的每一阶段不是依据某一个固定的递推式,而是在每一个阶段都看上去是一个最优的决策(在一定的标准下)。不断地将问题实例归纳为更小的相似的子问题,并期望做出的局部最优的选择产生一个全局得最优解。    贪心算法的适用的问题    贪

38、心算法适用的问题必须满足两个属性:    ()   贪心性质:整体的最优解可通过一系列局部最优解达到,并且每次的选择可以依赖以前做出的选择,但不能依赖于以后的选择。    ()   最优子结构:问题的整体最优解包含着它的子问题的最优解。    贪心算法的基本步骤    ()   分解:将原问题分解为若干相互独立的阶段。    ()   解决:对于每一个阶段求局部的最优

39、解。    ()   合并:将各个阶段的解合并为原问题的解。    问题分析    由于汽车是由始向终点方向开的,我们最大的麻烦就是不知道在哪个加油站加油可以使我们既可以到达终点又可以使我们加油次数最少。    提出问题是解决的开始.为了着手解决遇到的困难,取得最优方案。我们可以假设不到万不得已我们不加油,即除非我们油箱里的油不足以开到下一个加油站,我们才加一次油。在局部找到一个最优的解。却每加一次油我们可以看作是一个新的起点,用相同的递归方法进行下去。最

40、终将各个阶段的最优解合并为原问题的解得到我们原问题的求解。加油站贪心算法设计(C):    include<math.h>    include<studio.h>    int add(int b ,int m,int n)      /求一个从m到n的数列的和    int sb;    for(int i=m;i<n;i+) sb+=bi;  

41、0; return sb;        int Tanxin(int an, int N)  /an表示加油站的个数,N为加满油能行驶的最远距离        int bn;  /若在ai加油站加油,则bi为1,否则为0    int m=0;    if(ai>N) return ERROR;  /如果某相邻的两个加油站间的距离大于N,则不能到达终点  &#

42、160; if(add(ai, 0, n)<N)    /如果这段距离小于N,则不需要加油    bi=0;    return add(bi,0,n);        if(ai=aj&&ai=N)    /如果每相邻的两个加油站间的距离都是N,则每个加油站都需要加油    bi=1;    return add(bi,0,n);        if(ai=aj&&ai<N)    /如果每相邻的两个加油站间的距离相等且都小于N    if( add(ai,m,k) < N && add(ai,m,k+1) > N )       

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论