算法导论第15、16章习题—动态规划、贪心算法

发布于:2022-11-09 ⋅ 阅读:(13) ⋅ 点赞:(0) ⋅ 评论:(0)

算法导论第15、16章习题—动态规划、贪心算法

1. 我们对钢条切割问题进行一点修改,除了切割下的钢条段具有不同价格 p i p_i pi 外,每次切割还要付出固定的成本 c c c。这样,切割方案的收益就等于钢条段的价格之和减去切割的成本。设计一个动态规划算法解决修改后的钢条切割问题。
BOTTOM-UP-CUT-ROD(p,n,c)
	let r[0..n] be a new array
	r[0]=0  #钢管长度为0 收益为0
	for i = 1:n  #遍历钢管长度 求解规模为i的子问题
		temp=-inf
    for j = 1:i #访问数组直接获得规模为i-j的子问题的解
	        if j==i #问题规模相同时 不需要切割钢管
	        	temp=max(temp,p[j]+r[i-j])
	        else
	        	temp=max(temp,p[j]+r[i-j]-c)
	  	r[i]=temp
	return r[n]    

在原钢管切割的基础上考虑切割的成本 c c c,但在求解子问题时,若 i = j i=j i=j则不需要计算切割成本,具体内容如上述伪代码所示。

2. 把 M 个同样的苹果放在 N 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法? 5 , 1 , 1 5,1,1 5,1,1 1 , 5 , 1 1,5,1 1,5,1 是同一种分法。

f ( M , N ) f(M,N) f(M,N) M \text{M} M个苹果, N \text{N} N个盘子的放法数目,对N进行讨论,

N>M \text{N>M} N>M时,即盘子比苹果更多,那么至少有 N-M \text{N-M} N-M个盘子一直空着,去掉它们对摆放苹果方法数目不产生影响。即 if(N>M) f(M,N) = f(M,M) \text{if(N>M) f(M,N) = f(M,M)} if(N>M) f(M,N) = f(M,M)
N ≤ M N\le M NM时,即苹果比盘子更多,此时放法可以分成有盘子空着的方案以及没有空盘的方案
1. 有空盘时,则至少有一个盘子是空着的,所以 f(M,N) = f(M,N-1) \text{f(M,N) = f(M,N-1)} f(M,N) = f(M,N-1)
2. 没有空盘时,每个盘子都减少一个苹果不会影响不同的方案数,所以 f(M,N)=f(M-N,N) \text{f(M,N)=f(M-N,N)} f(M,N)=f(M-N,N)
N ≤ M N\le M NM时总体方案数等于上述两种情况之和,所以 if(N≤ M) f(M,N)=f(M,N-1)+f(M-N,N) \text{if(N≤ M) f(M,N)=f(M,N-1)+f(M-N,N)} if(N≤ M) f(M,N)=f(M,N-1)+f(M-N,N)
求解方法如下

fun(M, N) 
    if(M==0||N==1)
        return 1;
    if(N>M)
        return fun(M,M);
    else
        return fun(M,N-1)+fun(M-N,N);
3. 设计一个高效的算法,对实数线上给定的一个点集 { x 1 , x 2 , . . . x n } \{x1, x2,...xn\} {x1,x2,...xn}, 求一个单位长度闭区间的集合,包含所有给定的点,并要求此集合最小。证明你的算法是正确的。

对给定的点集先按升序排序,然后从最小点 x x x的开始,构建单位闭区间 [ x , x + 1 ] [x, x + 1] [x,x+1],再将在此区间中的所有数删除,重复此方法构建闭区间,直至点集为空,此时所有闭区间构成的集合就是满足要求的最小集合。
每次做出贪心选择后,剩余一个子问题:求剩余点构成的单位长度闭区间的最小集合,且该子问题总是存在最优解。假设存在一个最优解,其由若干闭区间构成,最小的点必然包含在最优解中最左的区间内,假设最小点是 x x x,这个区间为 [ m , n ] [m,n] [m,n], 如果这个点不是该最左区间的左端点,那么 m ≥ x m\ge x mx,所以 [ m , x ) [m, x) [m,x)不覆盖任何点,所以最左区间 [ m , n ] [m, n] [m,n]就是 [ x , x + 1 ] [x,x+ 1] [x,x+1],这个区间集是子问题的最优解。在这一修改过的最优解的基础上,从点集中取出包含在 [ x , x + 1 ] [x,x+ 1] [x,x+1]的点,同时从最优解中去掉 [ x , x + 1 ] [x,x + 1] [x,x+1],则现在的区间集合依然为修改后的点集的最优解,说明子问题最优解的组合可以得到原问题的最优解,所以该算法是正确的。

4. n 个作业组成的作业集,可由 m 台相同机器加工处理。设计一个比较好的调度策略,使所给的 n 个作业在尽可能短的时间内由 m 台机器加工处理完成。作业不能拆分成更小的子作业,即作业是非抢占的;每个作业均可在任何一台机器上加工处理。

当n ≤ \le m时,只要将作业时间区间分配给作业即可,此时每台机器最多处理一个作业即可完成任务,完成全部作业所需的时间为耗时最长的作业;
当n > > >m时,考虑每台机器都选择尚未完成的作业中最长的作业进行处理,即首先将n个作业按降序排序,然后依此顺序将作业分配给空闲的处理机,直到所有的作业全部处理完毕。这样可以保证处理机的利用率相对较高,因为如果每次是机器都是选择处理时间最短的作业分进行处理,可能会出现其它所有作业都处理完后,只剩所需时间最长的作业在处理的情况,这样种情况机器利用率较低。

5. 考虑用最少的硬币找 n n n 美分零钱的问题。假定每种硬币的面额都是整数。设计贪心算法求解找零问题,假定有 25 25 25 美分、 10 10 10 美分、 5 5 5 美分和 1 1 1 美分四种面额的硬币。证明你的算法能找到最优解。

每次取小于 n \text{n} n美分的最大面额的硬币 w \text{w} w,对剩余的 n-w \text{n-w} n-w重复上述过程,值得找零完成。在每次取最大面额硬币后,只剩一个子问题:用最少的硬币组合 n − w n-w nw面额,该问题存在最优解。
在最优解序列中,最多只能使用一个5美分硬币,否则可用10替代。同理有1美分最多使用4个;10美分最多两个;25美分可能使用任意个。所以只使用1美分的最优解,最多可找零4;只使用1、5美分的最优解,最多可找零9;只使用1、5、10美分的最优解,最多找零24。即:在题中所给出的面额下,不使用面额大于等于 w w w的硬币就无法表示大于等于 w w w的面额。所以如果有超过规定数目的各种类型的硬币,就可以用等值的数目更少的硬币来替换,即在求解最优解时,必然优先选择较大面额且不超过所需找零面额的硬币,若存在一个最优解使用的是更小面额的硬币,根据上述内容,将其换成更大面额且不超过找零的硬币可以得到一个硬币数更少的解,则得到一个更优的解,原最优解不成立。

6. 设定动态规划算法求解 0 − 1 0-1 01 背包问题,要求运行时间为 O ( n W ) O(nW) O(nW), n 为商品数量,W 是小偷能放进背包的最大商品总重量。

假设我们知道解决方案中有一个特定的重量 w w w。则必须求解最大重量为 W − w W − w Ww n − 1 n−1 n1项的子问题。因此采取自顶向下的方法,必须先解出所有商品重量小于 W W W 0 − 1 0-1 01背包子问题。因此需要构建一个 (n+1)*(W+1) \text{(n+1)*(W+1)} (n+1)*(W+1)的表,其中行为商品编号,列为从 0 0 0每次递增 1 1 1至最大重量的序列。(表的第一行和第一列全为 0 0 0)。
对于第 i i i j j j列,通过比较包括商品 1 1 1 i − 1 i−1 i1 的背包的总价值与最大重量 j j j,以及包括商品 1 1 1 i − 1 i−1 i1的总价值与最大重量 j − i . w e i g h t j−i.weight ji.weight 以及项目 i i i来决定在背包中包含项目 i i i是否有利。为解决这个问题,只需检查表的 n 、 W n、W nW 以确定可以取得的最大值。
伪代码如下:

0-1-KNAPSACK(n,W)
    new array K[n+1][w+1]
    for i = 1 to n	#初始化边界 重量为0时均为0
        K[i, 0] = 0
    for j = 1 to W	#初始化边界 数量为0时均为0
        K[0, j] = 0
    for i = 1 to n
        for j = 1 to W
            if j < Weight[i] #背包剩余容量不支持放入当前i商品
                K[i,j] = K[i-1,j]
            else #比较是不放入当前物品的价值更高/还是放入物品但减少容量后的价值更高
                K[i,j] = max(K[i-1,j], K[i-1,j-Weight[i]+Value[i])
			

在这里插入图片描述