钢条切割问题求解方法及相关思考

钢条切割问题求解方法及相关思考

题目来源于《算法导论》第15章第一节。问题如下:

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

问题1:一共有多少种切割方式?

思路一:对于一个长度为n英寸的钢条,其中一共有n-1个节点可供切割,在每一个节点处都可以选择切割或者不切割,将对一根钢条的切割过程视为从第一个节点直到第n-1个节点逐一选择切割或者不切割的一个过程,利用乘法原理,可以算出来总共有2n-1种切割方案。以四个节点的钢条为例:

思路二:也可以将切割一个长度为n英寸的钢条视作是从n-1个节点当中选择i(i=0,1,2,....,n-1)个节点进行分割的过程。那么总的分割方式m的计算方式可以使用排列组合的相关知识进行运算。m=C0n-1+C1n-1+.....+Cn-1n-1,根据牛顿二项式公式m=2n-1

问题解决方案

一、自顶向下递归

从钢条左边切割下长度为i的一段(不作任何处理),再对剩下的长度为n-i的钢条进行切割(递归),一个最优的解决方案就是:rn=max(pi+rn-i)(1<=i<=n),C++代码实现如下:

 1 #include "stdafx.h"
 2 int CutRod(int p[], int n)//输入分别为价格数组和问题规模(钢条大小)
 3 {
 4     if (n == 0)
 5     {
 6         return 0;
 7     }
 8     int q = -1000;//保证程序的顺利启动
 9     for (int i = 1; i <= n; i++)
10     {
11         if (q < p[i - 1] + CutRod(p, n - i))
12         {
13             q=p[i - 1] + CutRod(p, n - i);
14         }
15     }
16     return q;
17 }
18 int _tmain(int argc, _TCHAR* argv[])
19 {
20     int a[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 25 };//钢条分割的价格数组
21     printf("%d", CutRod(a, 10));
22     getchar();
23     return 0;
24 }

问题2:这样的一种递归遍历能否遍历所有的2n-1种切割方法?

我之前一直以为这种计算方法无法遍历到所有的切割方式,以为这种每一次都不考虑左半边分割的方式会忽略掉某些解。但是细想的话会发现,原来对于左半边的分割在之前的遍历当中已经考虑到了,并不需要再考虑。比如,如果从距离钢条左边2英寸处分割成两半然后只考虑右边的n-2英寸的钢条的分割的话,不需要考虑将左边2英寸的钢条再分为两个1英寸的情况,因为在计算将左边分为一个1英寸这种情况的时,另外的n-1部分其中有一种情况就是将其分为一个1寸和n-2寸的情况,这样就考虑了之前所说的那种情况。也可以对比问题一当中的例子来思考这一问题。

二、利用动态规划的思想解决这一问题

上面所提到的那种算法会在遍历的过程当中多次计算同样的子问题,所以需要应用一些策略来减少算法计算数量,下面分别提供了两种更好的方案。

带备忘的自顶向下的解决方案:记录在遍历的过程当中所遇到的子问题的解,算法在后面的运行当中会对照所记录的解,如果当前子问题存在解就直接输出解,否则就执行运算;

自底向上的解决方案:先解决子问题,再利用子问题的解来解决父问题;

1、带备忘的自顶向下的解决方案C++实现

 1 //对规模为n的问题进行计算
 2 int MCutRodA(int a[], int n, int r[])
 3 {
 4     int q;//问题的解
 5     if (r[n] >= 0)//如果子问题已经有解就返回所记录的子问题的解
 6     {
 7         return r[n];
 8     }
 9     if (n == 0)
10     {
11         q = 0;
12     }
13     else{
14         q = -100;
15         for (int i = 1; i <= n; i++)
16         {
17             if (q < MCutRodA(a, n - i, r) + a[i-1])
18             {
19                 q = MCutRodA(a, n - i, r) + a[i-1];
20             }
21         }
22     }
23     r[n] = q;//记录解
24     return q;
25 }
26 //带备忘的递归解决方案
27 int MCutRod(int a[],int n)
28 {
29     int* r = (int*)malloc((n + 1)*sizeof(int));//设置一个数组记录不同规模的子问题的解
30     for (int i = 0; i <= n; i++)
31     {
32         r[i] = -1;
33     }
34     return MCutRodA(a, n, r);
35 }
36
37 int _tmain(int argc, _TCHAR* argv[])
38 {
39     int a[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 25 };//钢条分割的价格数组
40     printf("%d", MCutRod(a, 10));
41     getchar();
42     return 0;
43 }

2、自底向上解决方案的C++实现

 1 int BTUCutRod(int a[], int n)
 2 {
 3     int* r = (int*)malloc((n + 1)*sizeof(int));
 4     r[0] = 0;
 5     for (int i = 1; i <= n; i++)//遍历所有可能的问题规模数
 6     {
 7         int q = -100;
 8         for (int j = 1; j <= i; j++)
 9         {
10             if ((a[j - 1] + r[i - j]) > q)
11             {
12                 q = a[j - 1] + r[i - j];
13             }
14         }
15         r[i] = q;
16     }
17     return r[n];
18 }
19 int _tmain(int argc, _TCHAR* argv[])
20 {
21     int a[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 25 };//钢条分割的价格数组
22     printf("%d", BTUCutRod(a, 10));
23     getchar();
24     return 0;
25 }
时间: 2024-12-23 06:45:36

钢条切割问题求解方法及相关思考的相关文章

动态规划学习笔记--对于钢条切割方案的思考

1.问题描述 对一个长为n的钢条,给出不同长度钢条对应的单价,求出如何切割能使得该钢条的收益最大化. 2.问题解析 (1)暴力法 找出所有切割方案(共2^(n-1)种),计算出每种切割方案的收益,求最大值. 时间复杂度:O(2^(n-1)) (2)动态规划 这一问题是<算法导论>中,讲解动态规划的例题. 为什么这题能够用动态规划解决呢? 动态规划是用来解决具有以下性质的问题的: 1.最优子结构 2.重复子问题 这两个性质,显然前者更为重要,而后者没有也可以用动态规划解决(我现在这么认为). 动

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

钢条切割问题 现有一段长度为n英寸的钢条和一个价格表pi,求切割方案使销售利益最大rn最大 长度为n英寸的钢条共有2n?1种不同的切割方案,因为可以每个整英寸的位置都可以决定切割或者不切割. 为了得到rn最大,可以把这个问题分成子问题求解,先切一刀,再考虑余下的部分的最大收益即求 rn=max{pk+rn?k}(k=1,2,3-n-1), pk部分不进行继续切割,直接作为一个整体售出 ; rn?k部分继续切割,考虑所有的情况,分成子问题. 求出所有k值对应的收益最大者作为rn 也有可能不进行任何

c++实现钢条切割问题

今天看了算法导论里面的动态规划(DP),有个有意思的问题:钢条切割来获取最大的收益. 书中讲到了几种求解方法,包括递归求解法.备忘录DP解法.自底向上的解法以及对解的重构.书中给出不同解法的伪码,刚好需要练习c++,就有c++来实现DP求解钢条切割问题. [递归求解] // 钢条切割问题 // 自顶向下 递归实现 #include <iostream> #include <time.h> using namespace std; int cut_rod(int len, int p

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

动态规划算法概述 动态规划(dynamic programming)1是一种与分治方法很像的方法,都是通过组合子问题的解来求解原问题.不同之处在于,动态规划用于子问题重叠的情况,比如我们学过的斐波那契数列.在斐波那契数列的求解问题中,我们经常要对一个公共子问题进行多次求解,而动态规划算法,则对每个子问题只求解一次,将其解保存在一个表格中,从而避免了大量的冗余计算量. 动态规划算法常用于寻找最优解问题(optimization problem).而其规划大概可分为四步: 1.刻画一个最优解的结构特

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

动态规划通常用于解决最优化问题,在这类问题中,通过做出一组选择来达到最优解.在做出每个选择的同时,通常会生成与原问题形式相同的子问题.当多于一个选择子集都生成相同的子问题时,动态规划技术通常就会很有效,其关键技术就是对每个这样的子问题都保存其解,当其重复出现时即可避免重复求解. 钢条切割问题 Serling公司购买长钢条,将其切割为短钢条出售.切割工序本身没有成本支出.公司管理层希望知道最佳的切割方案.假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元

算法导论读书笔记之钢条切割问题

算法导论读书笔记之钢条切割问题 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 给定一段长度为n英寸的钢条和一个价格表 pi (i=1,2, -,n),求切割钢条的方案,使得销售收益rn最大.注意,如果长度为n英寸的钢条价格pn足够大,最优解可能就是完全不需要切割. 若钢条的长度为i,则钢条的价格为Pi,如何对给定长度的钢条进行切割能得到最大收益? 长度i   1   2    3   4     5      6     7     8  

动态规划 钢条切割问题

#include <stdio.h> /* *钢条切割问题: *问题描述 假设公司出售一段长度为i英寸的钢条的价格为Pi(i = 1, 2, ...单位:美元),下面给出了价格表样例: 长度i 1 2 3 4 5 6 7 8 9 10 价格Pi 1 5 8 9 10 17 17 20 24 30 切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大. */ //假定价格一开始已经给出了,用全局的写死 #define max(x,y) (x)>

动态规划 -- 钢条切割

/* 动态规划和分治法相似,都是通过组合子问题的解来求解原问题. 但分治法是将 问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来,求出 原问题的解.与之相反,动态规划应用于子问题重叠的情况,即不同的子问题具有公共的 子问题.在这种情况下,分治法会做很多不必要的工作. 动态规划方法通常用来求解最优化问题,这类问题通常有很多可行解.我们希望寻找 具有最优值的解. 我们通常按照如下4个步骤来设计一个动态规划算法: · 刻画一个最优解的结构特征 · 递归地定义最优解的值 · 计算最优解

训练创新思维的方法:曼陀罗思考法

回顾10多年来走过的软件之路除了在经验上有一点积累.掌握了不少的技术之外似乎仍然一无所有,我并不是在传播负能量,这种一无所有指的并不是物质或是生活上的,而是在事业道路上.软件发展在于创新而这么多年来的工作却一直只是在跟随,自己意淫一下觉得比别人好的也只不过是跟随的速度会比较认识的人快那么一点点,时光推移到底什么东西是完全属于自己的呢 ?我也曾与不少至友讨论此问题,貌似也让大家陷入一些思考中.我们不缺技术.也不缺乏经验缺的可能是一种思想和意识那就是“持续创新的思维”.以前总认为自己在这方面是缺根筋