贪心算法详解

2023-05-16


  	之前讲过动态规划DP,现在来说说贪心。
	贪心算法在解决问题的策略上目光短浅,只根据当前已有的信息就做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。也就是说贪心对于算法的每一个决策点,每一次的选择,做一个当时看起来是最佳的选择。它并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。贪心算法对于大部分的优化问题都能产生最优解,但不能总获得整体最优解,通常可以获得近似最优解	我们先从DP来过渡到贪心,先来看一个例子,活动选择问题。
	假设有一个需要使用某个资源(教师等场地)的n个活动组成的集合S= {a1,a2,···,an },该资源每次只能由一个活动占用。每个活动a[i]都有一个开始时间s[i]和结束时间f[i],且0<= s[i] < f[i] <∞。一旦被选择后,活动a[i]就占据半开时间区间[s[i],f[i])。若时间区间[s[i], fi)与区间[sj, fj)互不重叠,则称活动a[i]与活动a[j]是兼容的。也就是说,当s[i]≥f[j]或s[j]≥f[i]时,活动a[i]与活动a[j]相容。活动选择问题就是要选择一个由兼容活动构成的最大集合。
例子

	活动都按结束时间非递减排序了(后面会讲why),这个例子的最大集合就是{a[1],a[4],a[8],a[11]}和{a[2],a[4],a[9],a[11]}。  


	很容易看出来这是一个DP问题,跟之前的求数组中最长递增子序列是类似的。状态转移方程就是LIS[i]=max{1,LIS[k]+1}   (a[i]和a[k]是兼容的for any k < i)。而且更重要的是我们还可以根据结束时间给每一个活动排序,这样代码就更容易写出来,这里就不贴了。  


	如果是这个DP方程的话,我们是没法从DP过渡到贪心的。我们再来看一个更好的DP算法。
	既然是DP,肯定要找状态转移方程。不过我们先来寻找子问题,什么是这个原问题的子问题呢?我们刚才说到,活动选择问题就是要选择一个由兼容活动构成的最大集合。子问题是什么,兼容活动集合,这样的子问题有2^n。所以我们才用DP来优化它,如果子问题的最优解可以构造成原问题的最优解,那么此问题就具有最优子结构。
	定义S[i,j]={a[k]∈S:f[i]<=s[k]<f[k]<=s[j]}(S是所有活动集合)。S[i,j]就是原问题集合S的子问题,其中的每个活动都是在活动a[i]结束之后开始,且在a[j]开始之前结束。更重要的是S[i,j]中的活动都要相互兼容。为了表示完整的问题结合,虚构两个活动a[0],a[n+1],f[0]=0,s[n+1]=∞。这样S=S[0,n+1]。  


	为了减少问题的处理量,给所以的活动按结束时间递增的顺序排序。这样的话如果i>=j,S[i,j]=∅。为啥呢?因为这是不可能的,假设有一个a[k]∈S[i,j],则f[i]<=sk<fk<=sj。说明a[i]活动排在a[j]活动前面,也即是i<j。这与i>=j矛盾。所以来说,如果将活动按结束时间非递减排序的话,则子问题就是S[i,j],0<=i<j<=n+1,其他的S[i,j]是空集。
	针对于S[i,j]中的a[k],我们把子问题分成S[i,k],S[k,j]和a[k]。则S[i,j]的最优解就是S[i,k]的最优解加上S[k,j]的最优解捎带一个a[k]的并集。证明子问题的最优解能构造原问题的方法就是剪贴法,不多说了。
	这样状态转移方程就有了,设C[i,j]是S[i,j]的最优解,即是S[i,j]中最大兼容活动数。当S[i,j]=∅(i>=j),此时C[i,j]=0。则状态转移方程就是C[i,j]=C[i,k]+C[k,j]+1
	虽然有了状态转移方程,但是对于每一个子问题都有k个选择,k=j-i-1(j-i+1-2),k=i+1....j-1。所以状态方程变了

	状态转移方程有了,这样很容易写出代码来,自底向上。  


	可是我们发现一个问题,那就是对于任何一个k将S[i,j]划分成两个子问题。可是如果有一个子问题是空的,那不是减少了我们的工作量吗?
定理:对于任何非空S[i,j],设a[m]是S[i,j]中最早结束的一个活动,f[m]=min{f[k]:a[k]∈S[i,j]},则
      1 活动a[m]被包含在S[i,j]的一个(可能有多个)最大兼容活动的子集中;
      2 子问题S[i,m]是空集,a[m]使S[m,j]成为仅有的非空子问题。
	简要证明一下:对于1,假设a[k]是S[i,j]最优解C[i,j]的第一个活动(排序之后),如果a[m]=a[k],结论得证.如果a[m],a[k]不相等,那么就用a[m]替换a[k](因为a[m]是最早结束的活动,替换之后肯定和其他的兼容),原问题的最大兼容活动数目没变,结论得证。对于2,很简单,一看就明白了。
	这个定理给我们带来了什么好处呢?好处就是两个子问题其中一个为空了,减少工作量了。还有就是最早结束的活动肯定就是当前子问题的最优活动!在之前的DP,我们每次要做j-i-1次选择,而现在我们只需要选择当前最早结束的活动即可。而且你选择了a[m],那么剩余问题的最优解就是S[m,j]的最优解。
	这样状态转移方程就是C[i,j]=C[m,j]+1(a[m]是S[i,j]中最早结束的活动,而且a[m]与最优解中之前的活动是兼容的,因为最早结束的 活动是当前子问题的一个最优活动,但是如果跟之前的已经找到的最优活动不兼容的话就没什么意义了吧)。这样还有一个好处就是代码不用在自底向上了,直接采用自顶向下来做就好了。我们找到了a[m],直接找S[m,j]的最优解就好了。
	这就是贪心算法,每次都选择当前最好的选择。对于原问题来说,a[1]肯定是原问题S[0,n+1]最优解的第一个,而a[2]就不一定是S[1,n+1]的最优解的一个,因为a[2]不一定与a[1]兼容。意思就是已经选定是最优活动,那么之后选择的最优要和之前选定的活动兼容。这样每次选择的活动都是和之前兼容,那所有的活动也就是只是考虑一次而已。

伪代码,每次选择的a[m]都要和a[i]兼容,而且是第一个和a[i]兼容的




  

求解过程图解  





  

递归解法很容易变成迭代算法,尤其是这种尾递归(几乎是)。  



时间复杂度都是O(n)。
	说完了这些,我们来总结一些贪心算法的特点以及解题步骤	贪心算法是通过一系列的选择来给出某一问题的最优解的。对于算法中的每一个决策选择都是做一个当时看起来最佳的选择,这种方法使我们常常能够得到最优解,但不是绝对的。DP才是绝对的。有人说每一个贪心算法的背后都有一个DP,这话一点没错。能用贪心解决的问题都可以用DP来解决。但是有些问题用贪心来解决更方便,更简单。
根据刚才的例子,我们可以总结出一些步骤,步骤都是灵活运用的,不是死的。
    1 决定问题的最优子结构
    2 设计出一个递归解。
    3 证明在递归的任一阶段,最优选择之一总是贪心选择,So贪心选择是安全的。
    4 证明通过贪心选择,所有子问题都是空(除了一个)
    5设计出一个实现贪心策略的递归算法。
    6 将递归算法转换成迭代算法。
简化步骤
    1 将优化问题转化成一个先做出选择,再解决剩下的一个子问题。
    2 证明原问题总是有一个最优解是贪心选择得到的,从而说明贪心选择的安全。
    3 说明在做出贪心选择之后,剩余的子问题具有这样一个性质。即如果将子问题的最优解和贪心选择结合起来,总是会得到原问题的一个最优解。
贪心算法的性质:
       1 最优子结构
	    对一个问题来说,如果他的最优解包含子问题的最优解,那该问题就就具有最优子结构性质。对于最优子结构的选择贪心和DP是不太一样的,贪心更加侧重于贪心选择和子问题的结合来得到全局最优解。
       2 贪心选择性质
	    一个全局最优解可以通过局部最优(贪心)选择来得到。也就是说我们做选择的时候是考虑当前问题的最佳选择,而不考虑子问题的结果。  


	DP和贪心的区别就是做选择的时候贪心所做出的选择是当前最佳,要依赖已经做出的所有贪心选择,而不依赖有待于做出选择的子问题的解。而DP具有无后效性,未来与过去无关,当前的状态是此前历史的一个完整总结,不会依赖已经得到子问题的解,只是和以后的子问题有关系,这点和贪心刚好相反。DP是通过小问题来得到大问题的解,而贪心是一次一次做出贪心选择,然后不断将给定的问题规约为更小的子问题。DP要自底向上,贪心可以自顶向下的解决问题。DP还具有重叠子问题的性质,这点是贪心不具备的。贪心只要满足这两点就可以来做,当然需要证明局部最优解是全局最优解。

	贪心算法的宗旨就是一个字----贪!一定要贪心,否则得不出最优解。
	贪心算法有很多的应用,比如哈夫曼编码,最小生成树,单源最短路径等,以后会陆续更新的。 
update
	POJ2287 田忌赛马---贪心算法	
	HDOJ1009 肥鼠的交易
	POJ1051 木棍叠加
转载请注明出处http://blog.csdn.net/liangbopirates/article/details/10044463


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

贪心算法详解 的相关文章

随机推荐

  • MRC和ARC

    MRC和ARC是两种设计模式 1 MRC设计模式 MRC xff1a Manul Reference Counting 手动引用计数 需要手动管理内存 xff0c 即手动添加release retain等内存管理代码 xff0c 否则 xf
  • 一款非常好用的动画库Lottie

    简介 Lottie是Android和iOS的移动图书馆 xff0c 用于解析Adobe After Effects动画 xff0c 并以Bodymovin作为json导出 xff0c 并在手机和网络上本机呈现 该项目在GitHub已经获得三
  • 深入浅出 HTTP协议

    好记忆不如烂笔头 xff0c 能记下点东西 xff0c 就记下点 xff0c 有时间拿出来看看 xff0c 也会发觉不一样的感受 目录 过程解说 体系介绍 域名解析 请求过程 问题解答 过程解说 先说下简要过程 xff0c 基本过程是如下所
  • linux,Windows11双系统安装及开机引导

    文章目录 前言系统安装UbuntuWindows 11 利用grub设置开机引导1 设置Ubuntu为默认启动系统2 设置开机引导grub3 找到Windows启动引导文件bootmgfw efi4 向grub cfg中添加menuentr
  • Docker常用命令,脚本在线或者离线安装Docker

    目录 常用命令停止容器 Docker镜像打包到另一台服务器 xff08 压缩包 xff09 Docker镜像打包到另一台服务器 xff08 使用Docker Hub xff09 Docker在线和离线安装卸载Docker 常用命令 span
  • docker镜像使用基础命令

    列出镜像列表 docker images REPOSITORY xff1a 表示镜像的仓库源 TAG xff1a 镜像的标签 用冒号分隔 版本标签 如果不指定就默认为latest IMAGE ID xff1a 镜像ID CREATED xf
  • 如何快速搜索文件和文件内容

    苏生不惑第144 篇原创文章 xff0c 将本公众号设为星标 xff0c 第一时间看最新文章 平常搜索文件一般会直接这样搜 xff0c 不过如果文件太多的话会很慢 xff0c 而且没法搜索文件内容 这里分享几个好用的文件搜索工具 Every
  • python3中替换python2中cmp函数的新函数分析(lt、le、eq、ne、ge、gt)

    本文地址 xff1a http blog csdn net sushengmiyan article details 11332589 作者 xff1a sushengmiyan 在python2中我们经常会使用cmp函数来比较一些东西 x
  • Eclipse中查看没有源码的Class文件的方法

    本文地址 http blog csdn net sushengmiyan article details 18798473 本文作者 sushengmiyan 我们在使用Eclipse的时候 xff0c 经常是会使用别人的Jar包 xff0
  • [ExtJS5学习笔记]第二节 Sencha Cmd 学习笔记 使你的sencha cmd跑起来

    本文地址 xff1a http blog csdn net sushengmiyan article details 38313537 本文作者 xff1a sushengmiyan 资源链接 翻译来源 Sencha Cmd官方网站 xff
  • 【Java二十周年】Delphi转行java的一些小感触

    本文纯属一届小码农对java使用过程的体验感触 目录 xff1a 初遇java编程语言与java的擦肩深入java 跨平台性开源支持web的支撑 初遇java编程语言 刚上大学的时候 xff0c 完全是个电脑盲 刚入学学的计算机普及知识就是
  • 给大家安利一个学习angular2的视频网站

    本文地址 xff1a http blog csdn net sushengmiyan 本文作者 xff1a 苏生米沿 视频地址 xff1a https egghead io courses angular 2 fundamentals 网站
  • 记一个万金油开源框架JHipster

    本文地址 xff1a http blog csdn net sushengmiyan article details 53190236 百搭代码生成框架 体验新技术汇总 xff1a Spring BootSpring SecurityAng
  • SQLServer触发器创建、删除、修改、查看...适用于级联删除

    一 触发器是一种特殊的存储过程 它不能被显式地调用 而是在往表中插入记录 更新记录或者删除记录时被自动地激活 所以触发器可以用来实现对表实施复杂的完整性约束 二 SQL Server为每个触发器都创建了两个专用表 Inserted表和Del
  • 工薪族巧理财之定期存款中整存整取、零存整取、存本取息之间的微妙区别

    银行的官方术语先给大家普及一下 xff1a 定期存款是在存款时约定存储时间 一次或按期分次 在约定存期 存入本金 xff0c 整笔或分期平均支取本金利息的一种储蓄 按存取方式定期存款分为整存整取定期存款 零存整取定期存款 存本取息定期存款
  • CentOS防火墙相关命令

    目录 1 开放端口2 查看防火墙所有开放的端口3 关闭防火墙4 查看防火墙状态5 查看监听的端口6 检查端口被哪个进程占用7 查看进程的详细信息 1 开放端口 firewall cmd zone span class token opera
  • no module named win32com.client错误解决

    无论什么时候 xff0c 你在运行的时候发现有importError no module named win32com client这个提示 你都可以这么解决 xff1a 请下载http sourceforge net projects p
  • java.util.concurrent同步框架(AQS论文中文翻译)

    java util concurrent同步框架 摘要目录和主题描述一般条款关键字1 介绍 xff1a 需求设计实现4 使用方式5 性能6 结论7 致谢 Doug Lea SUNY Oswego Oswego NY 13126 dl 64
  • POJ2287 田忌赛马---贪心算法

    田忌赛马 题目详见http poj org problem id 61 2287 田忌赛马大家都听过 xff0c 可是如果不是上中下三等马 xff0c 而是很多匹马 xff0c 优劣有很多种分类 xff0c 就不仅仅是321的问题了 这个很
  • 贪心算法详解

    之前讲过动态规划DP xff0c 现在来说说贪心 贪心算法在解决问题的策略上目光短浅 xff0c 只根据当前已有的信息就做出选择 xff0c 而且一旦做出了选择 xff0c 不管将来有什么结果 xff0c 这个选择都不会改变 也就是说贪心对