`
128kj
  • 浏览: 604421 次
  • 来自: ...
社区版块
存档分类
最新评论

贪心算法

阅读更多
    贪心算法是指从问题的初始状态出发,通过若干次的贪心选择而得出最优值(或较优解)的一种解题方法。并不是从整体上加以考虑,它所做出的选择只是在某种意义上的局部最优解。

   例如平时购物找钱时,为使找回的零钱的硬币数最少,不考虑找零钱的所有各种方案,
而是从最大面值的币种开始,按递减的顺序考虑各币种,先尽量用大面值的币种,当不足大面值币种的金额时才去考虑下一种较小面值的币种。这就是在使用贪婪法。这种方法在这里总是最优,是因为银行对其发行的硬币种类和硬币面值的巧妙安排。

   如只有面值分别为1、5和11单位的硬币,而希望找回总额为15单位的硬币。按贪婪算法,应找1个11单位面值的硬币和4个1单位面值的硬币,共找回5个硬币。但最优的解应是3个5单位面值的硬币。

    贪心算法可以简单描述为:对一组数据进行排序,找出最小值,进行处理,再找出最小值, 再处理。

    也就是说贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望得到结果是最好或最优的算法。经典的例子:
(1)最小生成树的Prim算法
http://128kj.iteye.com/blog/1667993
(2)最小生成树的Kruskal(克鲁斯卡尔)算法http://128kj.iteye.com/blog/1669539
(3)单源最短路径http://128kj.iteye.com/blog/1678532

    于一个具体的问题,怎么知道是否可用贪心算法来解决问题,以及能否得到问题的一个最优解呢?从许多可以用贪心算法求解的问题中可以看到它们一般具有两个重要的性质:
①贪心选择性质;②最优子结构性质。

1.贪心选择性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。每作一次贪心选择就将所求问题简化为规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

2. 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
问题的最优子结构性质是该问题可用贪心算法求解的关键特征。

贪心算法解题步骤:
①从问题的某个初始解出发;
②采用循环语句,根据局部最优策略,得到一个部分解,缩小问题的范围或规模;
③将所有部分解综合起来,得到问题的最终解。

例:最优服务次序问题

一、问题描述:
    设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti, 1≦i ≦n 。共有s处可以提供此服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n 个顾客等待服务时间的总和除以n。

二、贪心选择策略
   假设原问题为T(先假设只有一个服务点),而我们已经知道了某个最优服务系列,即最优解为
   A={t(1),t(2),….t(n)}
(其中t(i)为第i个用户需要的服务时间),则每个用户等待时间为:
   T(1)=t(1);T(2)=t(1)+t(2);...T(n)=t(1)+t(2)+t(3)+…+t(n);

那么总等待时问,即最优值为:
   TA=T(1)+T(2)+T(3)+...+T(n)=n*t(1)+(n-1)*t(2)+…+(n+1-j)*t(i)+…+2*t(n-1)+t(n);

    由于平均等待时间是n个顾客等待时间的总和除以n,故本题实际上就是求使顾客等待时间的总和最小的服务次序。

   本问题采用贪心算法求解,贪心策略如下:
   对服务时间最短的顾客先服务的贪心选择策略。首先对需要服务时间最短的顾客进行服务,即做完第一次选择后,原问题T变成了需对n-1个顾客服务的新问题T'。新问题和原问题相同,只是问题规模由n减小为n-1。基于此种选择策略,对新问题T',选择n-1顾客中选择服务时间最短的先进行服务,如此进行下去,直至所有服务都完成为止 。

三、问题的贪心选择性质
  先来证明该问题具有贪心选择性质,即最优服务A中t(1)满足条件:t(1)<=t(i)(2<i<n)。
用反证法来证明:假设t(1)不是最小的,不妨设t(1)>t(i)(i>1)。
设另一服务序列B=(t(i),t(2),…,t(1)…,t(n))
那么TA-TB=n*[t(1)-t(i)]+(n+1-i)[t(i)-t(1)]=(1-i)*[t(i)-t(1)]>0
即TA>TB,这与A是最优服务相矛盾。
故最优服务次序问题满足贪心选择性质。

四、问题的最优子结构性质
  在进行了贪心选择后,原问题T就变成了如何安排剩余的n-1个顾客的服务次序的问题T',是原问题的子问题。
若A是原问题T的最优解,则A'={t(2),…t(i)…t(n))是服务次序问题子问题T'的最优解。
   证明:假设A'不是子问题T'的最优解,其子问题的最优解为B',则有TB'<TA',
而根据TA的定义知,TA'十t(1)=TA。因此TB'+t(1)<TA'+t(1)=TA,
即存在一个比最优值TA更短的总等待时间,而这与TA为问题T的最优值相矛盾。因此,A'是子问题T'的最优值。

    从以上贪心选择及最优子结构性质的证明,可知对最优服务次序问题用贪心算法可求得最优解。

根据以上证明,最优服务次序问题可以用最短服务时间优先的贪心选择可以达到最优解。
故只需对所有服务先按服务时间从小到大进行排序,然后按照排序结果依次进行服务即可。平均等待时间即为TA/n。

五、算法实现
   由多处最优服务次序问题具有贪心选择性质和最优子结构性质,容易证明算法greedy的正确性。本算法采用最短服务时间优先的贪心策略。首先将每个顾客所需要的服务时间从小到大排序。然后申请2个数组:
st[]是服务数组,st[j]为第j个队列上的某一个顾客的等待时间;su[]是求和数组,su[j]的值为第j个队列上所有顾客的等待时间;

import java.util.Scanner;
import java.util.Arrays;
public class BestFuWu{
   static  public double greedy(int[] x,int s){
    int n=x.length;
    Arrays.sort(x);
    //System.out.println(Arrays.toString(x));
    int st[]=new int[n];
    int su[]=new int[n];
    int i=1,j=1;
    while(i<n){
	 st[j]+=x[i];
	 su[j]+=st[j];
	 i++;
	 j++;
	 if(j>s){
            j=(s!=1)?j%s:1;//循环分配顾客到每一个服务点上
         }
    }
    double t=0;
    for(i=1;i<=s;i++){
     System.out.println("第"+i+"个服务点队列上所有顾客的等待时间su["+i+"]="+su[i]);
        t+=su[i];
    }
    t/=(n-1);
    return t;
  }

  public static void  main(String[] args){
    Scanner in=new Scanner(System.in);
    int n;//等待服务的顾客人数
    int s;//服务点的个数
    double t;//平均服务时间
    System.out.println("请输入等待服务的顾客人数:");
    n=in.nextInt();
    int[] x=new int[n+1];
    System.out.println("请输入服务点个数:");
    s=in.nextInt();
    
    System.out.println("请输入每个顾客需要服务的时间:");
     for(int i=1;i<=n;i++){
	// System.out.println("No."+i);
	 x[i]=in.nextInt();
	
     }
    t=greedy(x, s);
   System.out.println("平均等待时间最小值="+t);
  }		
}


运行结果:

C:\test>java   BestFuWu
请输入等待服务的顾客人数:
10
请输入服务点个数:
1
请输入每个顾客需要服务的时间:
56 12 1 99 1000 234 33 55 99 812
第1个服务点队列上所有顾客的等待时间su[1]=5320
平均等待时间最小值=532.0

C:\test>java   BestFuWu
请输入等待服务的顾客人数:
9
请输入服务点个数:
3
请输入每个顾客需要服务的时间:
20 3 5 6 18 14 15 11 10
第1个服务点队列上所有顾客的等待时间su[1]=44
第2个服务点队列上所有顾客的等待时间su[2]=55
第3个服务点队列上所有顾客的等待时间su[3]=66
平均等待时间最小值=18.333333333333332

例: 活动安排问题
  设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,
而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si <fi 。如果选择了活动i,则它在半开时间区间[si, fi]内占用资源。若区间[si, fi]与区间[sj, fj]不相交,则称活动i与活动j是相容的。也就是说,当si≥fj或sj≥fi时,活动i与活动j相容。

   活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合,是可以用贪心算法有效求解的很好例子。该问题要求高效地安排一系列争用某一公共资源的活动。
贪心算法提供了一个简单、漂亮的方法使得尽可能多的活动能兼容地使用公共资源。

public class Activearr  
{  
    public static int greedselector(int [] s,int [] f,boolean [] a)  
    {  
        int n = s.length - 1;  
        a [0] = true;  
        int j = 0;  
        int count = 1;  
          
        for (int i = 1;i <= n;i ++)  
        {  
            if (s [i] >= f [j])  
          {  
            a [i] = true;  
            j = i;  
            count ++;  
               
          }   
          else a [i] = false;  
                  
        }  
          
        return count;  
      
    }  
    public static void main(String args [])  
    {  
        int count;  
        int s [] = {1,3,0,5,3,5,6,8,8,2,12};  
        int f [] = {4,5,6,7,8,9,10,11,12,13,14};  
        boolean a [] = new boolean [11];  
          
        //Activearr aa = new Activearr();  
        count = Activearr.greedselector(s,f,a);  
        System.out.println("共有" + count + "活动可以举行:");  
        System.out.println();  
        for (int i = 0;i <= 10;i ++)  
           if (a [i] == true)  
              System.out.println("第" + i + "活动可以举行");  
                
    }  
            
}  


程序运行:
共有4活动可以举行:

第0活动可以举行
第3活动可以举行
第7活动可以举行
第10活动可以举行

    由于输入的活动以其完成时间的非减序排列,所以算法greedySelector每次总是选择具有最早完成时间的相容活动加入集合A中。直观上,按这种方法选择相容活动为未安排活动留下尽可能多的时间。也就是说,该算法的贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。

       此算法的效率极高。当输入的活动已按结束时间的非减序排列,
算法只需O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。如果所给出的活动未按非减序排列,可以用O(nlogn)的时间重排。

下载源码:
分享到:
评论

相关推荐

    贪心算法 贪心算法 贪心算法 贪心算法

    贪心算法 贪心算法 贪心算法 贪心算法 贪心算法 贪心算法

    贪心算法、分治算法和动态规划的区别 贪心算法和动态规划.pdf

    贪心算法、分治算法和动态规划的区别 贪心算法、分治算法和动态规划是三种常用的算法设计策略,每种算法都有其特点和应用场景。下面我们将对这三种算法进行详细的比较和分析。 分治算法 分治算法是一种将原问题...

    用贪心算法解单源最短路径问题

    用贪心算法解单源最短路径问题 在计算机科学和信息技术领域中,单源最短路径问题是指从一个源点到其他顶点的最短路径问题。它是一种典型的图论问题,广泛应用于交通网络、通信网络、计算机网络等领域。贪心算法是...

    贪心算法之最优合并问题.zip

    贪心算法是计算机科学中解决问题的一种策略,它通过在每一步选择局部最优解来尝试达到全局最优解。这种算法在很多问题中表现出高效性,尤其是在处理优化问题时。本资料包"贪心算法之最优合并问题.zip"显然是针对贪心...

    贪心算法和动态规划以及分治法的区别? (1) 贪心算法和动态规划.pdf

    在计算机科学与算法设计领域,贪心算法、动态规划和分治法是三种常见的解决问题的方法,它们各自有着独特的策略和适用场景。本文将深入探讨这些算法之间的区别以及它们的优缺点。 首先,贪心算法是一种在每一步选择...

    贪心算法 贪心 算法 贪心的算法

    贪心算法是一种优化策略,它在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的。这种算法通常用于解决复杂问题,通过局部最优解逐步逼近全局最优解。贪心算法并不...

    tsp问题贪心算法求解

    **贪心算法与旅行商问题(TSP)** 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。它并不考虑整个问题的全局最优解,而是每一步都...

    贪心算法,最小生成树

    贪心算法和最小生成树 贪心算法是指在解决问题时,总是做出在当前看来最好的选择,以期望能够得到最终的最优解。贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。虽然贪心算法不能对...

    贪心算法 找零钱

    ### 贪心算法找零钱 #### 一、引言 在计算机科学与数学领域,贪心算法是一种解决问题的方法,它通过选择当前看起来最佳的选项来构建全局最优解。本篇文章将详细介绍如何使用贪心算法解决找零钱问题,并通过C语言...

    贪心算法 背包问题 c语言

    ### 贪心算法在背包问题中的应用及C语言实现 #### 一、贪心算法简介 贪心算法是一种在每个步骤中都选择局部最优解的策略,希望通过一系列的局部最优选择来达到全局最优解的目的。它适用于某些特定的问题类型,在...

    贪心算法求解tsp(旅行商问题)

    在TSP问题中,贪心算法可能会选择每次连接最近未访问的城市,但这种策略并不总是能得出最优解,因为贪心算法没有考虑到全局的最优路径规划。 在VC++环境下,实现TSP问题的贪心算法通常涉及以下步骤: 1. **数据...

    贪心算法 背包问题 C语言

    贪心算法是一种优化策略,它在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的。在解决背包问题时,贪心算法的应用尤为常见,尤其是对于部分背包问题。 0-1背包问题是最基础...

    数据结构中贪心算法实验报告

    贪心算法是数据结构和算法领域中的一种策略,它的核心思想是在每一步决策时都采取在当前状态下最好或最优(即最有利)的选择,希望通过这一系列局部最优的选择,最终达到全局最优的效果。在实验报告中,贪心算法被...

    经典的贪心算法,实用加常用

    经典的贪心算法,实用加常用 贪心算法(Greedy Algorithm)是一种经典的算法,在程序设计中非常常用。贪心算法的思想是,在对问题求解时,总是作出在当前看来是最好的选择。也就是说,不从整体上加以考虑,它所作出...

    贪心算法 部分背包问题

    贪心算法是计算机科学中解决问题的一种策略,它通过做出局部最优选择来逐步达到全局最优解。在部分背包问题中,这种策略尤其适用。部分背包问题是一个经典的优化问题,经常出现在运筹学、算法设计和分析中。在此问题...

    贪心算法之磁盘文件最优储存问题.zip

    贪心算法是计算机科学中解决问题的一种策略,它通过在每一步选择局部最优解来尝试达到全局最优解。在处理磁盘文件的存储问题时,贪心算法的应用旨在以最高效的方式组织文件,以减少磁盘读写的时间和空间消耗。本...

    用贪心算法求解哈密顿回路

    贪心算法是一种解决优化问题的策略,它在每一步选择中都采取在当前状态下最好或最优的选择,以期能得到全局最优解或近似最优解。 在本程序中,我们采用C语言实现了一个贪心算法来求解哈密顿回路的近似解。贪心算法...

Global site tag (gtag.js) - Google Analytics