一、引入

动态规划方法通常用来求解最优化问题(optimizationproblem)。这类问题可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值(最小值或最大值)的解。我们称这样的解为问
题的一个最优解(an optimal solution),而不是最优解( the optimal solution),因为可能有多个解都达到最优值。
我们通常按如下4个步骤来设计一个动态规划算法:
1.刻画一个最优解的结构特征。
2.递归地定义最优解的值。
3.计算最优解的值,通常采用自底向上的方法。
4.利用计算出的信息构造一个最优解。

15.1 钢条切割

一、问题相关

钢条切割问题是这样的:给定一段长度为n英寸的钢条和一个价格表p;(i=1, 2,… n),求切割钢条方案,使得销售收益rn最大。注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割。

二、初步想法

1、刻画最优解结构特征

(1)长度为n英寸的钢条共有2n-1种不同的切割方案,因为在距离钢条左端i(i=1,2,… n-1)英寸处,我们总是可以选择切割或不切割。我们用普通的加法符号表示切割方案,因7=2+2+3表示将长度为7英寸的钢条切割为三段一两段长度为2英寸、一段长度为3英寸。
(2)如果一个最优解将钢条切割为k段(对某个1≤k≤n),那么最优切割方案n= i + i2 +…+ ik将钢条切割为长度分别为i1,i2, …,ik的小段,得到最大收益rn= pi1 + pi2 +…+ pik对于上述价格表样例,我们可以观察所有最优收益值r(i=1,2,…,10)及对应的最优切割方案:
r1=1,切割方案1=1(无切割)
r2=5,切割方案2= 2(无切割)
r3=8,切割方案3=3(无切割)
r4=10,切割方案4=2+2
r5=13,切割方案5=2+3
r6=17,切割方案6= 6(无切割)
r7=18,切割方案7=1+6或7=2+2+3
r8=22,切割方案8=2+6
r9=25,切割方案9=3+6
r10= 30,切割方案10= 10(无切割)

(3)更一般地,对于rn(n≥1),我们可以用更短的钢条的最优切割收益来描述它:
rn= max(pn,r1+rn-1,r2+rn-2,…rn-1+r1)
(4)第一个参数pn对应不切割,直接出售长度为n英寸的钢条的方案。其他n-1个参数对应另外n-1种方案:对每个i=1, 2,…,n-1,首先将钢条切割为长度为i和n-i的两段,接着求解这两段的最优切割收益ri和rn-i;(每种方案的最优收益为两段的最优收益之和)。由于无法预知哪种方案会获得最优收益,我们必须考察所有可能的i,选取其中收益最大者。如果直接出售原钢条会获得最大收益,我们当然可以选择不做任何切割。
(5)注意到,为了求解规模为n的原问题,我们先求解形式完全一样,但规模更小的子问题。即当完成首次切割后,我们将两段钢条看成两个独立的钢条切割问题实例。我们通过组合两个相关子问题的最优解,并在所有可能的两段切割方案中选取组合收益最大者,构成原问题的最优解。我们称钢条切割问题满足最优子结构(optimal substructure)性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。
(6)一种更简单的办法:我们将钢条从左边切割下长度为i的一段,只对右边剩下的长度为n- i的- 一段继续进行切割(递归求解),对左边的一段则不再进行切割。即问题分解的方式为:将长度为n的钢条分解为左边开始一段,以及剩余部分继续分解的结果。

CUT-ROD(p,q)
if n==0return 0
q = -∞
for i = 1 to nq = max(q,p[i]+CUT-ROD(p,n-i))
return q

仔细一看这个方法并不是特别好,因为钢条一长,不断递归,问题规模巨大…

二、使用动态规划方法求解最优钢条切割问题

动态规划方法是典型的用空间争取时间,典型的时空权衡(time-memory-trade-off)。
有以下几种方法:
第一种方法称为带备忘的自顶向下法(top- down with memoization)曰。此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中)。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。我们称这个递归过程是带备忘的(memoized)。因为它“记住”了之前已经计算出的结果。
第二种方法称为自底向上法(bottom up method)。这种方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的"子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次, 当我们求解它(也是第一次遇到它)时, 它的所有前提子问题都已求解完成。

1、加入备忘机制的伪代码:

自顶向下

MEMORIZED-CUT-ROD(p,n)
let r[0...n] be a new array
for i = 0 to n //新建一个存储的数组r[i] = -∞
return MEMORIZED-CUT-ROD-AUX(p,n,r)
MEMORIZED-CUT-ROD-AUX(p,n,r)
if r[n] >= 0    //检查所需的值是否已知return r[n]
//计算所需值q
if n==0   q = 0
else q = -∞for i = 1 to nq = max(q,p[i]+MEMORIZED-CUT-ROD-AUX(p,n-i,r))
r[n] = q       //将q存入r[n]
return q

自底向上

BOTTOM-UP-CUT-ROD(p,n)
let r[0..n] be a new array  //创建一个新数组保存子问题的解
r[0] = 0
for j = 1 to nq = -∞for i = 1 to jq = max(q,p[i] + r[j-i]) //直接访问数组元素来获得子问题的解,r[j-i]一定是最优的r[j] = q
return r[n]

子问题图

c++代码

自顶而下

#include <iostream>
using namespace std;
int memorizedCutRodAux(int p[], int n, int r[])
{int q;//最大收益值if (r[n] >= 0)return r[n];//检查所需值是否已知if (n == 0)q = 0;//n=0时不会有收益else{q = -1;for (int i = 0; i < n; ++i)q = max(q, p[i] + memorizedCutRodAux(p, n - i - 1, r));}r[n] = q;return q;
}int memorizedCutRod(int p[], int n)
{int *r = new int(n+1);for (int i = 0; i <= n; ++i)r[i] = -1;return memorizedCutRodAux(p, n, r);
}int main()
{int p[10] = { 1,5,8,9,10,17,17,20,24,30 };int q = memorizedCutRod(p, 5);cout << "带备忘的自顶向下方法的最优收益值为:" << q;return 0;
}

自底而上

#include <iostream>
using namespace std;
int BottomUpCutRod(int p[], int n)
{int* r = new int(n + 1);r[0] = 0;if (n == 0) {return 0;}for (int j = 1; j <= n; j++) {int q = INT_MIN;for (int i = 1; i <= j; i++) {q = max(q, p[i-1] + r[j - i]);}r[j] = q;}return r[n];
}
int main()
{int p[10] = { 1,5,8,9,10,17,17,20,24,30 };int q = BottomUpCutRod(p, 4);cout << "带备忘的自底而上方法的最优收益值为:" << q;return 0;
}

2、进阶:不仅返回收益值,还返回第一段长度

EXTENDED-BOTTOM-UP-CUT-ROD(p,n)
let r[0...n] and s[0...n] be new arrays
r[0] = 0
for j = 1 to nq = -∞for i = 1 to jif q < p[i] + r[j-i]q = p[i] + r[j-i]s[j] = i       //将最优长度i保存于s[j]r[j] = q
return r and s

下面的过程接受两个参数:价格表p和钢条长度n,然后调用EXTENDED-BOTTOM-UP-CUT-ROD来计算切割下来的每段钢条的长度s[1…n],最后输出长度为n的钢条的完整的最优切割方案:
PRINT-CUT-ROD-SOLUTION(p,n)

PRINT-CUT-ROD-SOLUTION(p,n)
(r,s)= EXTENDED-BOTTOM-UP-CUT-ROD(p,n)
while n> 0print s[n]n=n-s[n]

C++代码

#include <iostream>using namespace std;void extendedBottomUpCutRod(int p[], int n, int r[], int s[])
{//int r[n+1];记录不同规模子问题的解,这里是1~10//int s[n+1];记录切割的解,即怎样切的int q;//记录收益r[0] = 0;for (int j = 1; j <= n; ++j){q = -1;//初始为负,常见的表示未知数的方法for (int i = 1; i <= j; ++i){if (q < p[i - 1] + r[j - i]){q = p[i - 1] + r[j - i];s[j] = i;}}r[j] = q;}
}void printCutRodSolution(int p[], int n, int r[], int s[])
{extendedBottomUpCutRod(p, n, r, s);cout << "长度为" << n << "的钢条按照此种切割方法的最优收益为:" << r[n] << endl;cout << "对应的最优切割方案的解为:";while (n > 0){cout << s[n] << " ";//输出最优切割方案n = n - s[n];}
}int main()
{int p[10] = { 1,5,8,9,10,17,17,20,24,30 };int r[11], s[11];printCutRodSolution(p, 10, r, s);return 0;
}

《算法导论》15章-动态规划 15.1 钢条切割(含有C++代码)相关推荐

  1. 算法复习——动态规划篇之钢条切割问题

    算法复习--动态规划篇之钢条切割问题 以下内容主要参考中国大学MOOC<算法设计与分析>,墙裂推荐希望入门算法的童鞋学习! 1. 问题背景 钢铁切割: ​ 现有一段长度为10的钢条,可以零 ...

  2. 《算法导论》中动态规划求解钢条切割问题

    动态规划算法概述 动态规划(dynamic programming)1是一种与分治方法很像的方法,都是通过组合子问题的解来求解原问题.不同之处在于,动态规划用于子问题重叠的情况,比如我们学过的斐波那契 ...

  3. 我眼中的算法导论 | 第一章——算法在计算中的作用、第二章——算法基础

    一个小白的算法学习之路.读<算法导论>第一天.本文仅作为学习的心得记录. 算法(Algorithm) 对于一个程序员来说,无论资历深浅,对算法一词的含义一定会或多或少有自己的体会,在< ...

  4. 算法导论2-9章补充几道题

    本篇博文意在对前几章中遗漏的,本人觉得有意思的习题当独拿出来练练手. 1.习题2-4,求逆序对,时间复杂度要求Θ(nlgn) 定义:对于一个有n个不同的数组A, 当i<j时,存在A[i]> ...

  5. 算法导论第二章部分习题自我解答

    练习: 2.3-2:去掉合并排序中的哨兵值 #include <iostream> using namespace std; void Merge(int* A, int p, int q ...

  6. 算法图解第九章动态规划读书笔记

    动态规划是一种解决棘手问题的方法,它将问题分成小问题,并先着手解决这些小问题 一.背包问题:一个背包的容量是4磅,吉他容量一磅,价值1500美元,音响容量4磅,价值3000美元,笔记本电脑容量3磅,价 ...

  7. 算法导论22章 基本图算法习题

    22.1-6  给出O(V)时间算法判断有向图G是否存在一个通用汇点(universal sink).通用汇点指的是入度为|V|-1,出度为0的节点. 思路: 考虑图的邻接矩阵A,假设i为通用汇点,则 ...

  8. 算法导论第九章习题9.2-3

    使用迭代版本的随机选择函数实现选择第i小元素.原理与9.2例题相同,该代码为9.2例题的迭代版实现.具体代码如下: #include<iostream> using namespace s ...

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

    写下文章来记录下自己学习算法导论的笔记 文章目录 写下文章来记录下自己学习算法导论的笔记 动态规划的目的 设计动态规划算法 钢条切割问题 问题描述 刻画问题结构(建立方程) 递归方程建立 带备忘录的自 ...

最新文章

  1. 正则表达式用户名密码电话身份证Email使用
  2. linux 远程 telnet
  3. ScrollView充满屏幕
  4. 网络编程 TCP学习
  5. ARM 嵌入式入门经验
  6. 以太坊是什么 - 以太坊开发入门指南
  7. LeetCode 895. 最大频率栈(哈希+按频数存储)
  8. 【Git】Git修改Repository语言类型
  9. 最新《机器学习最优化》课程笔记,38页pdf,Optimization for Machine Learning
  10. 核能版“水变油”登上Nature!谷歌7000万押注,MIT参与,被评争风加水汽车
  11. swift的可选项--optional/?
  12. 【记录】ASP.NET MVC MapRoute .htm 不起作用
  13. MATLAB机器人运动学与动力学(自己学习用)
  14. 2020年10月最新免费加速下载百度网盘文件方法
  15. 最新《择善教育》C/C++黑客编程项目实战教程
  16. Spring中AOP的Introductions使用介绍
  17. 摄像机标定:像素焦距与毫米焦距转换
  18. Burg法求解AR(p)模型参数(一)自回归模型
  19. 名帖30 东汉 隶书《西岳华山庙碑》
  20. NTFS下的USN日志文件研究

热门文章

  1. axure 元件_Axure动态面板(上下左右滑动页面)
  2. 写latex 遇到bib中参考文献 的俄文人名(类似于带有声调的拼音字母)如何转义?
  3. leetcode第643题C++
  4. 解决阿里云OSS使用URL无法访问图片
  5. 使用RDS for MySQL数据库(本地连接、数据库迁移、JDBC操作)
  6. 数据分析真题日刷 | 网易2018校园招聘数据分析工程师笔试卷
  7. lwip连续发数据卡死_用lwip发送大量数据时,遇到的问题解答记录;
  8. 极限的四则运算和洛必达法则的使用条件
  9. uva 10977 Enchanted Forest 魔幻森林
  10. 优化 MT4 性能让你的MT4更加流畅