16.1-5
给定n个活动的开始时间和结束时间表,及每个活动的收益表,求对这些活动的一个组织使收益最大化。
这问题其实CLRS正文介绍贪心算法时用到的 活动选择问题 的泛化;即能用贪心算法解决的活动选择是该问题的一个特例:收益都是1。
而这里,收益是各不相同的。于是直观地看,需要用到dp解决。
而因为活动之间存在兼容问题:j的开始时间有可能与 i 的结束时间或者开始时间冲突,所以对活动j,必须比较选择和不选择带来的收益。
可以定义一个数组,描述每一个活动 与 其他活动的兼容情况:compatible[ j ] = i 表示 与j 兼容的最大的 i,即 所有 finish [ i ] <= start [ j ]中,i的最大值。
于是 选择j的收益 = j的兼容活动的收益 + j本身的价值,而不选择j的收益 = j前一个活动的兼容活动集合带来的收益。
即 optimal [ i ] = max { value[i] + optimal [compatible [i] ] , optimal [ i-1 ] }
========================
代码如下:
package com.cupid.algorithm.dp; public class ActivitySelection { public static void ConstructSolution(int[] opt,int[] compatible,int[] v,int number){ if(number == 0){ System.out.println("done"); }else if(v[number] + opt[compatible[number]] > opt[number-1]){ System.out.println(v[number] + " by "+ " Activity " + number); ConstructSolution(opt, compatible, v, compatible[number]); }else{ ConstructSolution(opt, compatible, v, number-1); } } // CLRS 16.1-5 public static int ActivitySelectionMaximizingValue(int[] start,int[] finish,int[] v,int n,int[] opt){ int[] compatible = new int[n+1]; // Find out all compatible activities for each activity, // runs in O(N*lgN) for(int i=1;i<compatible.length;i++){ compatible[i] = binarySearchCompatible(finish,start[i]); } for(int i=1;i<opt.length;i++){ opt[i] = 0; if(opt[compatible[i]]+v[i] > opt[i-1]){ opt[i] = opt[compatible[i]]+v[i]; }else{ opt[i] = opt[i-1]; } } ConstructSolution(opt,compatible,v,n); return opt[n]; } // Given activity j's start time and finish time table, // do a binary search to find a compatible activity i with activity j, // where i is the largest number <= j private static int binarySearchCompatible(int[] finish,int startTime){ int highestCompatible = 0; int low = 0; int high = finish.length-1; int mid = 0; // Do binary search while(low<=high){ mid = (low+high)/2; // When low = high, // an exact finish time may or may not be found. // Assign value to highestCompatible accordingly. if(low==high){ // e.g. find 15 between 14,16 and finish[mid] = 16. // In this case return index of 14 if(startTime<finish[mid]){ highestCompatible = mid-1; // e.g. find 15 between 14,16 and finish[mid] = 14. // In this case return index of 14 }else if(startTime>finish[mid]){ highestCompatible = mid; } } if(startTime<finish[mid]){ high = mid-1; }else if(startTime>finish[mid]){ low = mid+1; }else{ highestCompatible = mid; break; } } return highestCompatible; } public static void main(String[] args) { int[] start = new int[]{-1,1,3,0,5,3,5,6,8,8,2,12}; // If finish-time array is not in order, sort it in O(N*lgN) int[] finish = new int[]{0,4,5,6,7,9,9,10,11,12,14,16}; // Different weights are given to different activities. int[] value = new int[]{0,3,2,4,8,2,5,6,5,7,4,5}; int[] optimal = new int[start.length]; optimal[0] = 0; int size = start.length-1; System.out.println("The maximum value is " + ActivitySelectionMaximizingValue(start, finish, value, size,optimal)); } }
====================
PS :标签上贪心只是与能用贪心解的权重为1的活动选择特例作比较。
参考:http://www.cs.princeton.edu/~wayne/cs423/lectures/dynamic-programming-4up.pdf