- 问题分析
1.1题目分析
一家公司购买长钢条,将其切割成短钢条出售,假设切割本身没有成本,长度为i的短钢条的价格为Pi。那给定一段长度为n的钢条和一个价格表Pi,求钢条的切割方案使得收益Rn最大。例如某公司以单价26元买到了一批长度为10的钢条,目前各长度钢条的市场价如下表所示:
长度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
价格Pi | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 26 |
要求:随机生成钢条长度n和不同长度钢条的价格信息,编写程序确定一种钢条的切割方案,使公司的收益最大化。
1.2思路分析
要解决最优解的值,通常用动态规划法解决此类问题。
动态规划则是通过组合子问题的解而解决整个问题的,递归算法也可以把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。
首先将钢条切割为长度为i和n - i两段,接着求解这两段的最优切割收益Ri和Rn - i(每种方案的最优收益为两段的最优收益之和),由于无法预知哪种方案会获得最优收益,因此必须考察所有可能的i,选取其收益最大者。如果直接出售原钢条会获得最大收益,也可以选择不做任何切割。
对于长度为n的钢条,我们可以先切一刀,切下长度为1-10的钢条,共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 = 27,最优切割方案10=2+2+6
对于Rn(n >= 1),可以用更短的钢条的最优切割收益来描述它:Rn = max(Pn, R1 + Rn-1, R2 + Rn-2,...)
- 解决方案
2.1 设计思路
通常用于具有某种最优性质问题,最后求出收益的最大化。对于这个题,一方面体现了最优解,另一方面体现了算法求解相同的子问题。使用动态规划算法时,用子问题的最优解来构造原问题的最优解和使用数组来保存子问题的解。动态规划与其他算法相比较而言,灵活性强,计算量大大减少了,但是对空间的需求增加了,本题因为所需空间不大,所以采用这种算法。
对于一个动态规划问题:
第一步就是先确定最优解的结构。如果一个问题的结构包含其子问题的最优解,就称此问题具有最优解的结构性质。使用动态规划算法时,用子问题的最优解来构造原问题的最优解。因此必须考查最优解中用到的所有子问题。
第二步定义最优解的计算公式。
第三步是根据得到的求解最优解公式,计算出结果。
第四步是构造出最优解。
2.2 解决方案
本问题要解决最优解的值的问题,动态规划通常用于解决此类问题,故可用动态规划解决此问题。动态规划则通过组合子问题的解而解决整个问题。又考虑到递归算法也可以把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。
1. 使用了rand()函数 。
2. 储存每个长度的钢条所能得到的最大价格。
3. 储存钢条价格的数组。
4. 优化代码,备忘录方法。
5. 随机生成价值范围。
6. 求出最大收益并输出。
三、遇到的问题及解决方案
3.1 遇到的问题
只用了分治策略的代码性能很差,在子问题的求解中很多都是重复的。
3.2 解决方案
动态规划方法对每个子问题只求解一次,并将结果保存下来。如果随后再次需要此子问题的解,只需查找保存的结果,而不必重新计算。因此,动态规划方法是付出额外的内存空间来节省计算时间。时间上的节省是非常巨大的:可能将一个指数时间的解转化为一个多项式时间的解。动态规划有两种等价的实现方法首先先考虑带备忘的自顶向下法。
而带备忘的自顶向下法:此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中)。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。此下是带备忘的自定向下法的代码,此代码添加自增的存储空间,在输出的同时也把计算的值记在备忘录里。
四、求解特色及关键技术
4.1 求解特色
1. 调用递归函数:int r[length+1]储存每个长度的钢条所能得到的最大价格
图4–1 调用递归函数
2.使用rand函数:
图4–2 使用rand函数
核心遍历:
图4–3 核心遍历
4.2 关键技术
动态规划、切割函数、随机数的生成
- 算法测试与应用
5.1 源代码
#include <stdio.h>
#include <stdlib.h>
const int length=10;
int r[length+1];
int v[length+1];
int s[length+1];
int main()
{
int i;
printf("钢条最大长度是%d\n",length);
for(i=1;i<length+1;i++)
{
v[i]=rand()%(3*i-v[i-1]+1)+v[i-1];
printf("钢条长度%d,价值%d\n",i,v[i]);
int i,j;
for(i=1;i<length+1;i++){
int result=v[i];
for(j=1;j<=i;j++){
if(result<(r[j]+r[i-j])){
result=r[j]+r[i-j];
}
}
r[i]=result;
}
}
printf("最高收益是%d\n",r[length]);
}
5.2 运行结果
图5–1代码运行结果
5.3 运算过程
1. 钢条长度为0时,价格为0;
2. 初始化时,将结果赋值为当前长度的钢条价格。0时不参与遍历;
3. resultmax(resultri+qiege(length--i)核心代码,遍历所有情况;
4. r[lengthresult/将当前长度的钢条能获得的最大价格储存在数组里。
六、结论
经过这半年来的算法课程的学习,通过线上线下的认真学习我们完成了预定的目标。
算法设计与分析是计算机科学与技术的一个核心问题。算法分析的学习对于培养逻辑思维能力有极大帮助,它可以培养我们分析问题,解决问题的能力。算法分析与设计是一门非常重要的课程。很多问题的解决,程序的编写都要依赖它。
算法可以使用自然语言、伪代码、流程图等多种不同的方法来描述。计算机系统中的操作系统以及各种各样的计算机应用系统中的软件,都必须使用具体的算法来实现。
通过对课程的理论学习与实践掌握了许多经典的算法思想,思维创新能力和实践能力得到了有效的提高,并且一题多解的情况对不同的算法有了更加深刻的认识。这些知识在今后的学习中将会得到更深层次的理解与应用。