热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

贪心算法在经典问题中的多种应用实例分析

本文深入探讨了贪心算法在多个经典问题中的应用实例,通过具体案例分析,展示了该算法在解决优化问题时的有效性和灵活性。同时,文章还结合Go语言的特点,为Golang开发者提供了实用的编程技巧和经验分享。

什么是贪心算法

贪心算法是一种在解决问题的过程中追求局部最优的算法,对于一个有多种属性的事物来说,贪心算法会优先满足某种条件,追求局部最优的同时希望达到整体最优的效果。以背包问题为例,可以放在背包中的物体有它的重量和价值两种属性,背包的容量也是有限的,我们希望得到一种价值最大的物品摆放方式,如果我们倾向于重量贪心,那么在摆放物品的时候会优先放重量小的,但这和我们追求的价值最优没有关系,自然不能采用;如果倾向于价值贪心,而忽略了物品的重量,可能会导致摆放物品的数量不多,总价值很小;如果是以价值和重量的比值设计贪心算法求解,便可以实现最优的方案。下面我们举一些例子来说明在实际运用中如何实践贪心算法。

例题1:钱币找零问题

1、题目:指定币值和相应的数量,用最少的数量凑齐某金额。
2、思路:利用贪心算法,我们优先选择面值大的钱币,以此类推,直到凑齐总金额。
3、算法实现:

   /**
     * 贪心算法1:钱币找零问题
     */
	public void greedy1(){        
                       //面额
			int[] values = { 1, 2, 5, 10, 20, 50, 100 };
			//数量
			int[] counts = { 3, 3, 2, 1, 1, 3, 3 };
			//获取需要各种面值多少张
			int[] result = getNumber1(446, values, counts);             
			System.out.println("各币值的数量:"+Arrays.toString(result));
	}

	/**
	 * 贪心算法1:钱币找零问题
	 * @param sum
	 * @param values
	 * @param counts
	 * @return
	 */
	public int[] getNumber1(int sum , int[] values, int[] counts)
	{
		int[] result = new int[7];
		int add=0; //当前凑的金额
		for(int i=values.length-1;i>=0;i--)
		{
		    int num = (sum-add)/values[i];
		    if(num>counts[i])
		    {
		    	num=counts[i];
		    }
			add=add+num*values[i];
	        result[i]=num;
		}
		return result;		
	}

例题2:活动选择问题

1、题目: 有n个需要在同一天使用同一个教室的活动a1,a2,…,an,教室同一时刻只能由一个活动使用。每个活动ai都有一个开始时间si和结束时间fi ,一旦被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这一天,该问题就是要安排这些活动使得尽量多的活动能不冲突的举行。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。
在这里插入图片描述
2、思路:使用贪心算法,目标是实现安排尽可能多的活动,那么我们优先找那些结束时间早的活动,为后面的活动留出更多时间,即以结束时间为贪心。
3、算法实现:
注:这里我们稍打乱了顺序,在代码中采用了插入排序的方法对数据简单整理,使得结束时间从小到大排列。

	/**
	 * 贪心算法2:活动选择问题
	 */
	public void greedy2(){		  
			   int [] st = {1,5,0,5,3,3,6,8,8,2,12};
			   int [] et = {4,9,6,7,8,5,10,12,11,13,14};			
			   int num = getNumber2(st,et);
			   System.out.println("活动数量:"+num);		
	}
	/**
	 * 贪心算法2:活动选择问题
	 * @param a
	 * @param b
	 * @return
	 */
	public int getNumber2(int[] a , int[] b)  //优先选择结束时间早的
	{
		int num=0;
		int tempa=0;
		int tempb=0;
		int endTime=0;
		int j=0;
		for(int i=1;i<b.length;i++)//如果顺序混乱,则调整为结束时间从小到大的顺序,直接插入排序
		{
			    tempb=b[i];
				tempa=a[i];
			for(j=i-1;j>=0&&tempb<b[j];j--)
			{								
					b[j+1]=b[j];
					a[j+1]=a[j];
					if(j==0)
					{
						j--;
						break;
					}
			}
			    b[j+1]=tempb;
				a[j+1]=tempa;				
		}
		System.out.println(Arrays.toString(a));
		System.out.println(Arrays.toString(b));		
		num++;
		endTime=b[0];
		for(int k=1;k<b.length;k++)
		{
			if(a[k]>endTime)
			{
				num++;
				endTime=b[k];				
			}
		}						
		return num;								
	   }	
     }

例题3:背包问题

1、题目:现有几种拥有一定重量和价值两个属性的物品,需要放到一个容量一定(能承受的重量一定)的包中,物品放入包中时,物品可以不完全放入包中,而放入一部分,求价值最大的方案。
2、思路: 背包问题一般不能使用贪心算法。 然而我们考虑这样一种背包问题:在选择物品i装入背包时,可以选择物品的一部分,而不一定要全部装入背包。这时便可以使用贪心算法求解了。 计算每种物品的单位重量价值作为贪心选择的依据指标,选择单位重量价值最高的物品,将尽可能多的该物品装入背包,依此策略一直地进行下去,直到背包装满为止。 在零一背包问题中贪心选择之所以不能得到最优解原因是贪心选择无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了。 在程序中已经事先将单位重量价值按照从大到小的顺序排好。
3、算法实现:

	/**
	 * 贪心算法3:背包问题
	 */
	public void greedy3(){
			    float M=50;  
			    //背包所能容纳的重量     
			    float[] w={0,10,30,20,5};  
			    //每种物品的重量    
			    float[] v={0,200,400,450,20};    
			    //每种物品的价值   
				   
				float num = getNumber3(M,w,v);   
				 
				System.out.println("物品数量:"+num);
	}
	/**
	 * 贪心算法3:背包问题
	 * @return
	 */
	public float getNumber3(float M,float[] w ,float[] v)
	{
		float num=0;
		int i=0;
		float max=0;
		float weight=0;
		for(i=0;i<w.length;i++)
		{
			if(v[i]/w[i]>max)
			{
				max=v[i]/w[i];
				weight=w[i];
			}
		}
		num=M/weight;
		return num;	
	}

例题4:多机调度问题

1、题目:n个作业组成的作业集,可由m台相同机器加工处理。要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
2、思路:作业不能拆分成更小的子作业;每个作业均可在任何一台机器上加工处理。 这个问题是NP完全问题,还没有有效的解法(求最优解),但是可以用贪心选择策略设计出较好的近似算法(求次优解)。当n<=m时,只要将作业时间区间分配给作业即可;当n>m时,首先将n个作业从大到小排序,然后依此顺序将作业分配给空闲的处理机。也就是说从剩下的作业中,选择需要处理时间最长的,把它分配给当前总累计需要工作时长最短的机器。这样一来,这个调度问题可以理解为一个分配问题,我们通过这种方案,使得几台机器获得接近的工作总时长,达到整体的最短的工作时长的效果。
3、算法实现:

	/**
	 * 贪心算法4:多机调度问题
	 */
	public void greedy4()
	{
		       int[] time= {9,7,8,4,2,1,3};
		       int number = 3;
		       
		       int Sumtime = getNumber4(time,number);
		
		       System.out.println("花费的最小总时间:"+Sumtime);		
	}
	 /**
	 * 贪心算法4:多机调度问题
	 * @param time
	 * @param number
	 * @return
	 */
	public int getNumber4(int[] time, int number)
	{
		int usedTime=0;  //最长时间为总时间
		int[] fin = new int[number]; //单机处理时间		
		for(int k=0;k<number;k++) //初始时间清零
		{
			fin[k]=0;
		}		
		if(number>time.length)
			return time[0];
		else 
		{			
			for( int i=0 ; i<time.length-1 ;i++)
			{  
				   for( int j=0;j<time.length-i-1;j++) //冒泡选出任务时间最大的
				   {
					   if(time[j]>time[j+1])
					   {
						   int temp = time[j+1];
						   time[j+1]=time[j];
						   time[j]=temp;
					   }
				   }
					   int min=0;; 
					   int value=100;
					   for(int k=0;k<fin.length;k++)  //选出当前累计工时最小的机子
					   {
						   if(fin[k]<value)
						   {
							   min=k;
							   value=fin[k];
						   }						  
					   }					   						
					   fin[min]+=time[time.length-1-i];							   
			} 
			   int min=0;; 
			   int value=100;
			   for(int k=0;k<fin.length;k++)  //选出当前累计工时最小的机子
			   {
				   if(fin[k]<value)
				   {
					   min=k;
					   value=fin[k];
				   }				  
			   }
			   fin[min]+=time[0];
			for( int n=0;n<fin.length;n++)
			{
				if(fin[n]>usedTime)
				{
					usedTime=fin[n];
				}
			}
			return usedTime;
		}		
	}

贪心算法5:小船过河问题

1、题目:N个人过河,船每次只能坐两个人,船载每个人过河的所需时间不同t[i],每次过河的时间为船上的人的较慢的那个,求最快的过河时间。(船划过去要有一个人划回来)
2、思路:本题的最优选择是先将所有人过河所需的时间按照升序排序。优先把速度慢的人带到对岸,返回由速度快的人来完成,节省时间,在剩余人数大于3时,有两种方式: 1.最快的和次快的过河,然后最快的将船划回来;次慢的和最慢的过河,然后次快的将船划回来,所需时间为:t[0]+2t[1]+t[n-1];2.最快的和最慢的过河,然后最快的将船划回来,最快的和次慢的过河,然后最快的将船划回来,所需时间为:2t[0]+t[n-2]+t[n-1]。最后还需处理一下人数小于等于3的边界问题。
3、算法实现:

	/**
	 * 贪心算法5:小船过河问题
	 */
	public void greedy5()
	{
		       int[] v = {1,3,4,8,4,3,9}; //按照不同的人的速度过河所需的时间
		       int timeSum=getNumber5(v);
		       System.out.println("过河总时间:"+timeSum);
	}
          /** 
	 * 贪心算法5:小船过河问题
	 */
	public int getNumber5(int[] v)
	{
		int time =0;;
		Arrays.sort(v);//降序排列
		int N = v.length; //N表示当前人数
		while(N>3)
		{
			if(2*v[0]+v[N-1]+v[N-2]>2*v[1]+v[0]+v[N-1])
				time+=2*v[1]+v[0]+v[N-1];
			else
				time+=2*v[0]+v[N-1]+v[N-2];
			N-=2;
		}
		else if(N==3) //处理边界
		{
			time+=v[2]+v[0]+v[1];
		}
		else if(N==2)
		{
			time+=v[1];
		}
		else if(N==1)
		{
			time+=v[0];
		}	
		return time;		
	}

总结

贪心算法追求局部最优,拿到问题之后先分析我们需要达到什么目标,是否适合采用贪心算法,并且使得什么最优以及实现的方法。


推荐阅读
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 最近团队在部署DLP,作为一个技术人员对于黑盒看不到的地方还是充满了好奇心。多次咨询乙方人员DLP的算法原理是什么,他们都以商业秘密为由避而不谈,不得已只能自己查资料学习,于是有了下面的浅见。身为甲方,虽然不需要开发DLP产品,但是也有必要弄明白DLP基本的原理。俗话说工欲善其事必先利其器,只有在懂这个工具的原理之后才能更加灵活地使用这个工具,即使出现意外情况也能快速排错,越接近底层,越接近真相。根据DLP的实际用途,本文将DLP检测分为2部分,泄露关键字检测和近似重复文档检测。 ... [详细]
  • 本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ... [详细]
  • 本文探讨了《魔兽世界》中红蓝两方阵营在备战阶段的策略与实现方法,通过代码展示了双方如何根据资源和兵种特性进行战士生产。 ... [详细]
  • 本题通过将每个矩形视为一个节点,根据其相对位置构建拓扑图,并利用深度优先搜索(DFS)或状态压缩动态规划(DP)求解最小涂色次数。本文详细解析了该问题的建模思路与算法实现。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 作为一名专业的Web前端工程师,掌握HTML和CSS的命名规范是至关重要的。良好的命名习惯不仅有助于提高代码的可读性和维护性,还能促进团队协作。本文将详细介绍Web前端开发中常用的HTML和CSS命名规范,并提供实用的建议。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • 本文提供了使用Java实现Bellman-Ford算法解决POJ 3259问题的代码示例,详细解释了如何通过该算法检测负权环来判断时间旅行的可能性。 ... [详细]
  • Java编程实践:深入理解方法重载
    本文介绍了Java中方法重载的概念及其应用。通过多个示例,详细讲解了如何在同一类中定义具有相同名称但不同参数列表的方法,以实现更灵活的功能调用。 ... [详细]
  • 本文介绍如何在Linux Mint系统上搭建Rust开发环境,包括安装IntelliJ IDEA、Rust工具链及必要的插件。通过详细步骤,帮助开发者快速上手。 ... [详细]
  • 在 Flutter 开发过程中,开发者经常会遇到 Widget 构造函数中的可选参数 Key。对于初学者来说,理解 Key 的作用和使用场景可能是一个挑战。本文将详细探讨 Key 的概念及其应用场景,并通过实例帮助你更好地掌握这一重要工具。 ... [详细]
  • 深入理解Shell脚本编程
    本文详细介绍了Shell脚本编程的基础概念、语法结构及其在操作系统中的应用。通过具体的示例代码,帮助读者掌握如何编写和执行Shell脚本。 ... [详细]
author-avatar
你看看我的世界_420
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有