一、问题分析
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 解题思路
动态规划:
对于长度为n的钢条,我们可以先切一刀,切下长度为1-10的钢条,共10种切法,
最大收益就是切下的钢条收益和剩下钢条的最大收益之和。
钢条长度为1的最大收益计算出来,保存到dp[1]
长度为2的钢条,遍历每一种切法,收益最大的保存到dp[2]
计算长度为n的钢条的最大收益,此时dp数组已经保存了1——n-1长度钢条的最大收益
遍历这10种切法,就可以找到最大的收益
由于每种钢条长度的最大收益都被保存在数组dp中,避免了很多的重复计算
二、问题的解决方案/算法选择/设计思路
2.1 算法选择
动态规划原理
①最优子结构
用动态规划求解最优化问题的第一步就是刻画最优解的结构,如果一个问题的解结构包含其子问题的最优解,就称此问题具有最优子结构性质。因此,某个问题是否适合应用动态规划算法,它是否具有最优子结构性质是一个很好的线索。使用动态规划算法时,用子问题的最优解来构造原问题的最优解。因此必须考查最优解中用到的所有子问题。
②重叠子问题
如果递归算法反复求解相同的子问题,就称为具有重叠子问题(overlapping subproblems)性质。在动态规划算法中使用数组来保存子问题的解,这样子问题多次求解的时候可以直接查表不用调用函数递归。
2.2 设计思路
对于一个动态规划问题:
第一步先确定最优解的结构。如果一个问题的解结构包含其子问题的最优解,就称此问题具有最优子结构性质。使用动态规划算法时,用子问题的最优解来构造原问题的最优解。因此必须考查最优解中用到的所有子问题。
第二步定义最优解的计算公式
第三步根据得到的求最优解公式,计算出结果。
第四步构造出最优解。
2.3 解决方案
对于长度为n的钢条,我们可以先切一刀,切下长度为1-10的钢条,共10种切法,
最大收益就是切下的钢条收益和剩下钢条的最大收益之和。
1. 钢条长度为1的最大收益计算出来,保存到dp[1]
2. 长度为2的钢条,遍历每一种切法,收益最大的保存到dp[2]
3. 计算长度为n的钢条的最大收益,此时dp数组已经保存了1——n-1长度钢条的最大收益
遍历这10种切法,就可以找到最大的收益
由于每种钢条长度的最大收益都被保存在数组dp中,避免了很多的重复计算。
三、算法设计
3.1 设计方案图
图3-1 |
3.2 源代码
创建伪代码
#include<iostream>
#include<cstring>
using namespace std;
int pi[] = {0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 26};
// 1. 递归求解
//int cut_rod(int n)
//{
// if(n == 0) return 0;
// int max_value = -1;
// for(int i=1; i<=n; i++){
// max_value = max(max_value, v[i]+cut_rod(n-i));
// }
// return max_value;
//}
// 2.动态规划
/*
对于长度为n的钢条,我们可以先切一刀,切下长度为1-10的钢条,共10种切法,
最大收益就是切下的钢条收益和剩下钢条的最大收益之和。
1. 钢条长度为1的最大收益计算出来,保存到dp[1]
2. 长度为2的钢条,遍历每一种切法,收益最大的保存到dp[2]
3. 计算长度为n的钢条的最大收益,此时dp数组已经保存了1——n-1长度钢条的最大收益
遍历这10种切法,就可以找到最大的收益
由于每种钢条长度的最大收益都被保存在数组dp中,避免了很多的重复计算,
*/
int dp[1000];
int cut_rod(int n)
{
if(n == 0) return 0;
int max_value = -1;
for(int i=1; i<=n; i++){
for(int j=1; j<=i && j<=10; j++)
max_value = max(pi[j]+dp[i-j], max_value);
dp[i] = max_value;
}
return max_value;
}
int main()
{
int n;
cin >> n;
int re = cut_rod(n);
cout << re << endl;
return 0;
}
四、算法设计/求解特色及关键技术
对于长度为n的钢条,我们可以先切一刀,切下长度为1-10的钢条,共10种切法,
最大收益就是切下的钢条收益和剩下钢条的最大收益之和。
1. 钢条长度为1的最大收益计算出来,保存到dp[1]
2. 长度为2的钢条,遍历每一种切法,收益最大的保存到dp[2]
3. 计算长度为n的钢条的最大收益,此时dp数组已经保存了1——n-1长度钢条的最大收益
遍历这10种切法,就可以找到最大的收益。
由于每种钢条长度的最大收益都被保存在数组dp中,避免了很多的重复计算。