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