算法导论 习题 16.1-5

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是两个问题。让我分别回答。 --- 对于习题24.2-4,我们需要证明:对于有向图$G=(V,E)$,从源点$s$到任何其他点$v\in V$的最短路径上最多可以包含$|V|-1$条边。 我们可以使用反证法来证明这个结论。假设存在一条从$s$到$v$的最短路径包含了$|V|$条边。由于这是一条简单路径,所以它经过了$|V|$个顶点中的所有顶点。因此,这个路径形成了一个简单环。我们可以从这个简单环中删除任意一条边,得到一条从$s$到$v$的路径,路径长度比原来的最短路径长度更小,这与原来的最短路径的假设相矛盾。因此,假设不成立,结论得证。 --- 对于习题24.3-6,我们需要证明:如果负权重有向图$G$中不存在从源点$s$可达的负权重环,则Bellman-Ford算法能够正确地计算出从$s$到所有其他顶点的最短路径。 我们可以使用反证法来证明这个结论。假设存在一个从$s$到$v$的最短路径上存在一个负权重环。由于负权重环的存在,我们可以通过不断绕这个环走来无限制地减小路径长度,因此不存在从$s$到$v$的最短路径。但是,Bellman-Ford算法会在第$|V|$次松弛操作之前终止,并且在第$i$次松弛操作之后,算法会计算出从$s$到所有距离$s$不超过$i$的顶点的最短路径。因此,我们可以得出结论:如果负权重有向图$G$中不存在从源点$s$可达的负权重环,则Bellman-Ford算法能够正确地计算出从$s$到所有其他顶点的最短路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值