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

2023-11-15

一、引入

动态规划方法通常用来求解最优化问题(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==0
	return 0
q = -∞
for i = 1 to n
	q = 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 n
		q = 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 n
	q = -∞
	for i = 1 to j
		q = 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 n
	q = -∞
	for i = 1 to j
		if 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> 0
	print 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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • 无法使用已与其底层 RCW 分离的 COM 对象。在 oledb 中

    我收到此错误 但我不知道我做错了什么 下面的代码在backrgroundworker中 将异常详细信息复制到剪贴板 System Runtime InteropServices InvalidComObjectException 未处理 通
  • C# 和 Javascript SHA256 哈希的代码示例

    我有一个在服务器端运行的 C 算法 它对 Base64 编码的字符串进行哈希处理 byte salt Convert FromBase64String serverSalt Step 1 SHA256Managed sha256 new S
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • C - 找到极限之间的所有友好数字

    首先是定义 一对友好的数字由两个不同的整数组成 其中 第一个整数的除数之和等于第二个整数 并且 第二个整数的除数之和等于第一个整数 完美数是等于其自身约数之和的数 我想做的是制作一个程序 询问用户一个下限和一个上限 然后向他 她提供这两个限
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • 如何返回 json 结果并将 unicode 字符转义为 \u1234

    我正在实现一个返回 json 结果的方法 例如 public JsonResult MethodName Guid key var result ApiHelper GetData key Data is stored in db as v
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • 将自定义元数据添加到 jpeg 文件

    我正在开发一个图像处理项目 C 我需要在处理完成后将自定义元数据写入 jpeg 文件 我怎样才能做到这一点 有没有可用的图书馆可以做到这一点 如果您正在谈论 EXIF 元数据 您可能需要查看exiv2 http www exiv2 org
  • clang 实例化后静态成员初始化

    这样的代码可以用 GCC 编译 但 clang 3 5 失败 include
  • 如何在 VBA 中声明接受 XlfOper (LPXLOPER) 类型参数的函数?

    我在之前的回答里发现了问题 https stackoverflow com q 19325258 159684一种无需注册即可调用 C xll 中定义的函数的方法 我之前使用 XLW 提供的注册基础结构 并且使用 XlfOper 类型在 V
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问

随机推荐

  • NP完全问题的证明-算法概论课后习题8.15

    题目 证明如下问题是NP 完全的 输入 两个图G1 V1 E1 和G2 V2 E2 预算b 输出 两个节点集合V1 V1 和V2 V2和它们被移除后 将在两图中分别留下至少b个节点 且图的剩余部分完全一样 解析 可将最大独立集问题归约到此问
  • 多态原理探究

    概念 当类中声明虚函数时 编译器会在类中生成一个虚函数表 虚函数表是一个存储类成员函数指针的数据结构 虚函数表是由编译器自动生成与维护的 virtual成员函数会被编译器放入虚函数表中 当存在虚函数时 每个对象中都有一个指向虚函数表的指针
  • FFmpeg简介及在vc2010下编译步骤

    FFmpeg是一个开源的多媒体库 最新版本是2 4 3 它的License是LGPL或GPL FFmpeg可以用来记录 转换数字音频 视频 并能将其转换为流的开源计算机程序 它包括了音 视频编码库libavcodec FFmpeg是在Lin
  • [mmdetection 混合精度]用fastrcnn实测混合精度fp16效果2

    用官方检测工具测试 平均时间 0 0947 0 1298 0 72958 map比较 faster rcnn r50 fpn 1x coco py fp16训练后map 结论 map下降不明显 但平均训练时间降低了27 fp16还是很好的
  • uni-app混合开发中的链接跳转navigateTo、reLaunch、redirectTo、switchTab区别

    1 navigateTo 保留当前页面 跳转到应用内的某个页面 使用uni navigateBack可以返回到原页面 要注意的是navigateTo只能跳转的应用内非 tabBar 的页面的路径 路径后可以带参数 如果跳转url参数为tab
  • kubeadm 安装 kubernetes 1.4.6

    kubeadm 安装 kubernetes 1 4 6 准备 安装docker 下载镜像 安装kubernetes 安装kubernetes dashbord 准备 机器名 ip centos7 kubermaster 192 168 10
  • C语言入门--3x3转置矩阵

    题目 答题思路 我这里使用的是两个多维数组 因此此段代码仅需修改输入部分既可用于多种对称矩阵的转置 第一个多维数组用于记录原始的矩阵排列 第二个多维数组用于记录转置后的矩阵排列 我的思路很简单 先将右上角的数交换到左下角 本人非数学专业 专
  • Redis学习;Redis主从复制

    主从复制 是指将一台Redis服务器的数据 复制到其他的Redis服务器 前者称为主节点 Master Leader 后者称为从节点 Slave Follower 数据的复制是单向的 只能由主节点复制到从节点 主节点以写为主 从节点以读为主
  • QAnimation的介绍

    QAnimation的介绍 QAnimation是Qt框架中提供的一个动画类 用于实现GUI控件的各种动画效果 可以通过QAnimation实现如平移 旋转 缩放等动态效果 同时还支持动态添加或删除控件等操作 基本用法 创建和启动动画 通过
  • Libsvm:脚本(subset.py、grid.py、checkdata.py)

    1 脚本 This directory includes some useful codes 1 subset selection tools 子集抽取工具 subset py 2 parameter selection tools 参数选
  • c#基础之WPF

    学习平台 微软开发者博客 DevBlogs Microsoft Developer Blogs 微软文档与学习 Microsoft Learn 培养开拓职业生涯新机遇的技能 微软开发者平台 Microsoft Developer WPF基础
  • Java-String类

    Java String类 1 概述 String 字符串 使用一对 引起来表示 String声明为final的 不可被继承 String实现了Serializable接口 表示字符串是支持序列化的 实现了Comparable接口 表示Str
  • Iptables封禁IP,记录地址

    前言 前段时间开了台国外的vps 以前机房的物理防火墙有十年网工大佬维护 我们对外访问的nginx上就没遇到过这种攻击 毕竟有问题的IP全被防火墙那层直接拦截了 而我这通过安全组把其中2个web端口放开了所有网段 而nginx再限制网段 导
  • 疫情日记(01)

    时间 2022年12月16日15 01 14 天气 阴天 地点 西安 12月8日全面放开以来 确诊这个词语也再是谈之色变了 似乎一旦 规定 疫情没有了 就真的没有了一样 两天前知道了ljc可能是确诊了 再后来就是办公室两个小兄弟 jc和qy
  • FAT文件系统初识

    最近在阅读 现代操作系统 的时候看到了fat32系统的讲解 在这里记录一下 我觉得fat32文件系统首先是基于链表分配的机制的 首先有一个基础知识 就是文件是由一系列的块组成的 想要访问完整的文件 就必须知道这个文件的所有的块的位置 链表分
  • Unity3d C#使用XCharts数据显示格式说明(如:数据类型、数据显示为百分比%等)

    前言 XCharts是开源且比较强大的插件 在Unity3d中搭建UI时常常使用的数据图表的制作插件 特别是当下的数字沙盘 数字孪生等项目中应用较广 笔者公司也一直在使用该插件 本文主要是在开发过程中的一个小需求引发的整理分享 在项目中需要
  • 前端自动化测试精讲

    单元测试 端对端测试 持续集成方案 在项目中落地前端自动化测试 作者介绍 祯民 字节跳动前端开发工程师 掘金小册 SSR实战 官网开发指南 作者 公众号 祯民讲前端 作者 曾负责 抖音前端技术团队官网 和 字节官网中文版 的开发 现维护抖音
  • git撤销加入暂存区(git add)的文件

    直接使用git reset加对应的文件或 来撤销 这个命令可以理解为git add的反向操作 可以撤销单个文件 也可批量 如 git reset xxx xxx xxx 或 git reset
  • C语言常见问题——++i与i++详解

    目录 一 i与i 1 引例 2 i i i 与 i i i 3 总结 二 函数中的 1 printf中的 2 作为函数的参数 3 总结 一 i与i 1 引例 对于如下程序 其输出结果是什么 include
  • 《算法导论》15章-动态规划 15.1 钢条切割(含有C++代码)

    一 引入 动态规划方法通常用来求解最优化问题 optimizationproblem 这类问题可以有很多可行解 每个解都有一个值 我们希望寻找具有最优值 最小值或最大值 的解 我们称这样的解为问 题的一个最优解 an optimal sol