看了下贪心算法,直觉上以为适用于用贪心算法解决的问题好像并不多啊,不过现在先不说这个。先讨论下动态规划和贪心算法的不同之处,下面是一些本人结合书本得出的体会:
1、动态规划通常是自底向上求解问题的(当然也可以是"带备忘"的自顶向下求解问题),每一次选择都面向多个子问题选择,只不过这些子问题的解都是基于那些已经求解的子子问题的解。从本质上说动态规划遍历了所有的可能解,只是在求解子问题时使用了“子子问题的解",从而避免了在求解重复子问题上浪费的时间(相对于分治法而言)。但是贪心算法则不然,贪心算法是自顶向下求解的,每次进行一次贪心选择后,只面向一个子问题。
2、既然贪心算法只需要面向一个子问题,它的算法时间也只有theta(n),那么我们都用贪心算法吧,多快啊。可是我们知道既然它又这么快的速度,说明它必定只在某个特定的环境下才适用,我们要做的全部工作就是论证。论证问题适用于贪心算法。
3、如果进行贪心算法时,我们不得不考虑多种选择,通常意味着可以改进贪心选择。使其更高效。如:在本文中,求解活动安排问题的前提时,活动的结束时间都以单调递增的顺序排列好了。如果不是这样的话,我们在一次贪心选择之后必须面向多个选择。
SO,和动态规划有两个特性一样,贪心算法也有两个特性:贪心选择性和最优子结构特性。说实话,现在我还不理解贪心选择性,说所求问题的全局最优解可以通过一系列的局部最优(贪心)选择组成。我的疑惑就是如果已经能够证明问题具有贪心选择性,还需要最优子结构特性吗?直接进行迭代的弹性求解不就好了吗?或者可以说,是否最优子结构特性只是贪心算法问题的一个特性而已,即这个问题如果是可以贪心算法求解最优解的,那么它必定具有最优子结构特性。
话说,贪心算法问题的主要工作就是证明问题的贪心选择性和最优子结构特性。可事实是现在我对证明还不是很清楚,只能边写边总结了。估计多写几次也就会证明了。不扯了,转入正题吧,关于活动安排问题。
问题描述:现有一个阶梯教室,有n个活动(A1A2A3A4...An)需要申请这个教室,但是这个教室同一时间只能被一个活动使用。已知每个活动的起始时间Ts和结束时间Te。问怎样安排活动才能使尽可能多的活动使用到教室。
问题解决:
1、为了使得每次贪心选择后面向唯一的一个子问题,我们将活动按结束时间单调递增排序。
2、活动安排问题的贪心算法特性证明:
贪心选择性:由于结束时间的单调递增排序,可以知道第一次贪心选择的是A1,如果能够证明A1必属于某一个全局最优解(这里的全局最优解可能有多个),就证明了问题的贪心选择特性。怎么证明呢?设已有全局最优解Sk,其中Aj是Sk中结束时间最早的活动,讨论A1和Aj,如若给A1=Aj,则问题已证。如果A1 != Aj。因为A1.endTime <= Aj.endTime。那么即使将Aj替换成A1得到的新的Sk'应该也是相容的,因为Sk'包含的活动个数等于Sk包含的活动个数,所以Sk‘也是一个全局最优解,则特性得证,即A1必属于某个全局最优解。进而推论,每次做出的局部贪心选择都属于全局最优解中的一个子问题。
好了,代码如下:
package com.wly.algorithmbase.greedy;import java.util.ArrayList;/*** 贪心算法解活动安排问题* 问题:现有一个阶梯教室可供活动使用,同一时间最多只有一个活动能使用该阶梯教室* 现有n个活动需要都使用该阶梯教室,先一个每个活动的开始和结束时间,求怎样安排活动才能使使用阶梯教室的活动最多。* @author wly**/ public class ActivitySelection {public static void main(String[] args) { Activity[] activities = new Activity[5]; activities[0] = new Activity(0,0); activities[1] = new Activity(1,2); activities[2] = new Activity(3,7); activities[3] = new Activity(2,9); activities[4] = new Activity(8,10);ArrayListresultList = new ArrayList (); resultList = resolve(activities); for(Activity a:resultList) { System.out.println("(" + a.getStart() + "," + a.getEnd() + ") "); } }/*** 求解问题* @param startTimes 活动的开始时间数组* @param endTimes 活动的结束时间数组* @return*/ public static ArrayList resolve(Activity[] activityList) { int n = activityList.length; ArrayList result = new ArrayList ();int k = 1; result.add(activityList[1]); for(int m=2;m = activityList[k].getEnd()) { result.add(activityList[m]); k = m; } } return result; } }class Activity { int start; int end;public Activity(int start, int end) { super(); this.start = start; this.end = end; } public int getStart() { return start; } public void setStart(int start) { this.start = start; } public int getEnd() { return end; } public void setEnd(int end) { this.end = end; } }
运行结果:
(1,2) (3,7) (8,10)