算法导论-动态规划(钢条切割问题)

  动态规划(dynamic programming)与分之方法相似,都是通过组合子问题的解来求解原问题。分治方法将问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来,求出原问题的解(如归并排序)。而动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治算法会做许多不必要的工作,它会反复地求解那些公共子子问题。而动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作。

    通常按如下4个步骤来设计一个动态规划算法:

    1.刻画一个最优解的结构特征。

    2.递归地定义最优解的值。

    3.计算最优解的值,通常采用自底向上的方法。

    4.利用计算出的信息构造一个最优解。

例子:钢条切割问题

    描述:给定一段长度为n英寸的钢条和一个价格表pi(i = 1,2,3...,n)求切割钢条方案,使得销售收益rn最大。

    钢条价格表

长度i12345678910
价格pi1589101717202430

   如果一个最优解将钢条切割为k段(1≤k≤n),那么最优切割方案 n = i+ i2 + i3 +...+ ik,得到最大收益 r = pi1 + pi2 + pi3 + ... + pik。更一般地,对于 rn(n ≥ 1),我们可以用更短的钢条的最优切割收益来描述它:rn = max(pn , r1 + rn-1 , r2 + rn-2, ..., rn-1 + r1),第一个参数pn对应不切割,直接出售长度为n的钢条的方案。其他n-1个参数对应另外n-1种方案:对每个i  = 1,2,...,n-1,首先将钢条切割为长度为i 和 n -i 的两段,接着求解这两段的最优切割收益r和rn-i。由于无法预知哪种方案会获得最优收益,我们必须考察所有可能的i,选取其中收益最大者。

    注意到,为求解规模为n的原问题,我们先求解形式完全一样,但规模更小的子问题。即当完成首次切割后,我们将两段钢条看成两个独立的钢条切割问题实例。通过组合两个关于子问题的最优解,并在所有可能的切割方案中选取组合收益最大者,构成原问题的最优解。动态规划方法仔细安排求解顺序,对每个子问题只求解一次,并将结果保存下来。是付出额外的内存空间来节省计算时间。有两种等价的实现方法:

       一,带备忘的自顶向下法。此方法按自然的递归形式编写过程,但过程会保存每个子问题的解,当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。

       二,自底向上法。这种方法一般需要恰当定义子问题"规模"的概念,使得任何子问题的求解只依赖于"更小"的子问题的求解。因而可以将子问题按规模排序,按由小到大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。

自底向上法代码如下:

 1 void bottomUpCutRod(const vector<int> &p,int n , vector<int> &r)
 2 {
 3     r.resize(n+1);
 4     r[0] = 0;
 5     int q = -1;
 6 
 7     for(int j = 1; j <= n; ++j){
 8         q = -1;
 9         for(int i = 1; i <= j; ++i){
10             q = std::max(q,p[i] + r[j-i]);
11         }
12         r[j] = q;
13     }
14 }

例子:

1 int main()
2 {
3     int p[] = {-1,1,5,8,9,10,17,17,20,24,30};
4     vector<int> vp(p,p + sizeof(p)/sizeof(int));
5     vector<int> result;
6     bottomUpCutRod(vp,9,result);
7     for(int i = 1; i <=9; ++i)
8         cout << result[i] << " ";
9 }

结果保存在result数组中,长度为i的钢条切割的最优收益值为result[i]。

输出:

 

转载于:https://www.cnblogs.com/zlm-jessie/p/5658048.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值