算法导论15.1动态规划之钢条切割

动态规划与钢条切割

1.分治算法与动态规划

相同点:

都是通过组合子问题的解来求解原问题

不同:

1.分治将问题划分为互不相交的子问题,递归地求解子问题,在将它们的解组合起来,求出原问题。

2.动态规划应用于子问题重叠的情况,即不同的子文问题具有公共的子问题。(这种情况下,分治会反复求解那些共子问题,而动态规划将每个子问题的解保存在表格中,从而无需重复计算子问题)

2.钢条切割

image-20220321153020264

问题:给定一段长度为n英寸的钢条和一个价格表 p i ( i = 1 , 2 , 3 , … , n ) p_i(i=1,2,3,…,n) pi(i=1,2,3,,n),求切割钢条的方案使收益最大

设计动态规划的过程:

  • 刻画一个最优子结构的特征–>递归地定义最优解的值–>计算最优解的值(通常使用自底向上的方法)

    –>利用计算的信息构造最优解

  • 切割方案: 2 n − 1 2^{n-1} 2n1

    在距离钢条左端 i ( i = 1 , 2 , 3 , … , n ) i(i=1,2,3,…,n) i(i=1,2,3,,n)处,我们总可以选择切割或不切割

  • 描述切割“长度为n的钢条”的最大收益 r n r_n rn(刻画一个最优解的结构特征)

    设最优解将钢条切割成k段,最优切割方案为: n = i 1 + i 2 + i 3 + … i k n=i_1+i_2+i_3+…i_k n=i1+i2+i3+ik,

    则有最大收益: r n = p i 1 + p i 2 + … + p i n r_n=p_{i_1}+p_{i_2}+…+p_{i_n} rn=pi1+pi2++pin

  • 利用更短的钢条的最优切割来描述 r n r_n rn(递归地定义最优解的值)

    r n = m a x ( p n , r 1 + r n − 1 , r 2 , r n − 2 , … r n − 1 + r 1 ) r_n=max(p_n,r_1+r_{n-1},r_2,r_{n-2},…r_{n-1}+r_1) rn=max(pn,r1+rn1,r2,rn2,rn1+r1)

    1.第一个参数对应不切割,直接出售长度为n英寸的钢条

    2.其他参数对应将钢条切成i和n-i的两端的各个切法

  • 最优子结构特征

    我们称钢条切割问题满足最优子结构的特征:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解

  • 简化 r n r_n rn的递归定义

    r n = max ⁡ 0 ≤ x ≤ n ( p i + r n − i ) r_n=\max_{0 \leq x \leq n} (p_i+r_{n-i}) rn=max0xn(pi+rni)

    即钢条的切割方式为:从左边切下长度为i的一段(且不再进行切割),从右边切下长度为n-i的一段,继续进行切割。若没有切割,则第一段长度为n,收益为 p n p_n pn,第二段长度为0,收益为0;

3.自定向下递归实现
int cutRod(int price[], int n){
	if(n==0)
    return 0;
 	int max_val = INT_MIN;
  for(int i=0;i<n;i++){
    max_val =max( max_val,p[i]+cutRod(price,n-i));
	} 
  return max_val;
}

image-20220321164400928

可以看到CUT-ROD反复求解重复子问题,设T(0)=1,有

image-20220321164753020

利用归纳法可以证明 T ( n ) = 2 n T(n)=2^n T(n)=2n

4.使用动态规划求解

两种等价的实现方法:

  • 带备忘录的自定向下法

    此方法仍然按照自然的递归形式编写,但过程会保存每个子问题的解。当需要一个子问题的解时,过程首先检验是否保存过此解。

    image-20220321170418254

    image-20220321170436235

  • 自底向上法

    任何子问题的求解都依赖更小的子问题的求解,当求解某个子问题时,它所依赖的更小子问题都已求解完毕


#include<iostream>
#include <bits/stdc++.h>
#include<math.h>
using namespace std;

// A utility function to get the maximum of two integers
int max(int a, int b) { return (a > b)? a : b;}

/* Returns the best obtainable price for a rod of length n and
price[] as prices of different pieces */
int cutRod(int price[], int n)
{
int val[n+1];
val[0] = 0;
int i, j;

// Build the table val[] in bottom up manner and return the last entry
// from the table
for (i = 1; i<=n; i++)
{
	int max_val = INT_MIN;
	for (j = 0; j < i; j++)
		max_val = max(max_val, price[j] + val[i-j-1]);
	val[i] = max_val;
}

return val[n];
}

/* Driver program to test above functions */
int main()
{
	int arr[] = {1, 5, 8, 9, 10, 17, 17, 20};
	int size = sizeof(arr)/sizeof(arr[0]);
	cout <<"Maximum Obtainable Value is "<<cutRod(arr, size);
	getchar();
	return 0;
}

5.子问题图

image-20220321164400928image-20220321170928253

我们可以将动态规划方法的子问题图看成自定向下递归树的“收缩版”,所有相同子问题合并为同一节点。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值