问题描述:假设公司出售长度为i的钢条价格为pi,如下价格表:
长度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
价格pi | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 30 |
给一个n长度钢条和一个价格表,求切割方案使得总收益最大。
分析:当问题长度为n时候,每隔一米有两种选择(切或者不切),从而切割方案有2n种,如果用暴力来求解,复杂度会达到指数次方,比如一个长度为4的钢条可以有以下切割方案:
(1)4=1+1+1+1(2)4=1+3(3)4=2+2(4)4=3+1(5)4=1+1+2(6)4=1+2+1(7)4=2+1+1(8)4=4;
比较刻字方案(3)的收益最高为10,从而方案(3)为最佳切割方案。
为啦求解规模为n的问题,我们可以求解问题一样但是规模更小的问题,当我们完成首次切割后,我们将剩下钢条看做两个独立的钢条切割问题,我们通过求解两个子问题最优解来构造原问题最优解,及说明此问题
有最优最结构的性质:我们将钢条从左切割下长度为i的一段,只对右边长度为n-i的一段进行继续切割,问题分解方式为:将问题从左边切割下一段,将右边部分继续做相同的分解,若剩余长度为0则收益也为0,
Rn=max{p[i]+R[n-i]}(1=<i<=n);
方法1,自顶向下:
int CutRod(int price[], int *r, int n){
int q =price[n],i,t; //设计q初始化为不切割情况
if (r[n] >= 0)//所有r[1...n]都初始为负数,若此次不是负数,说明已经计算过,不用在计算,从而节省啦大量时间
return r[n]; //如果计算过直接返回
else{
for (i =1; i <n; i++){
t =price[i]+CutRod(price, r,n-i); //子问题的解
if (q < t)
q = t;
}
}
r[n] = q; //该问题的解
cout << n << " : " << r[n] << endl; //输出当前长度下的造价收益
return q;
}
该问题中通过判断r[i](1<=i<=n)是否大于0来判断r[i]是否有计算过,如果计算过就不用重新计算,这样大大提高啦运行效率,保准啦每个子问题只计算一次.
方法2:自底向上:
int CutRod2(int price[], int *r, int n){
int q,i,j,t;
r[0] = 0; //长度为0的钢条收益为0
for (i = 1; i <=n; i++){
q = -1; //初始为负数
for (j = 1; j <= i; j++){
t = price[j] + r[i - j]; //计算长度为i的钢条的切割方案收益
if (t>q)
q = t;
}
r[i] = q; //计算完长度为i的钢条的造价收益保存
}
return q;
}
两种代码的时间复杂度都是O(n2).如果要重构解的问题,输出具体的切割方案,则还需要一个数组s[n]来保存切割方案:
int *CutRod2(int price[], int *r, int n){
int q,i,j,t;
r[0] = 0; //长度为0的钢条收益为0
for (i = 1; i <=n; i++){
q = -1; //初始为负数
for (j = 1; j <= i; j++){
t = price[j] + r[i - j]; //计算长度为i的钢条的切割方案收益
if (t>q)
q = t;
}
r[i] = q; //计算完长度为i的钢条的造价收益保存
}
return s;
}
如果要构造问题解的方案,这需要一个数组s[n]来保存切割的位置:
//----钢条切割2,动态规划
#include<iostream>
#include<fstream>
using namespace std;
int CutRod2(int price[], int *r,int*s ,int n);
int main(){
ifstream sin("a.txt");
int n, i, price[100], r[1000],s[1000];
sin >> n;
for (i = 1; i < n + 1; i++)
sin >> price[i];
int R = CutRod2(price, r,s, n);
for (i = 0; i <= n; i++)
cout << i << " :" << r[i] << endl;
n = 9; //输出长度为9的钢条切割方案
while (s[n]>0){
cout << s[n] << " ";
n = n - s[n];
}
return 0;
}
int CutRod2(int price[], int *r,int *s, int n){
int q,i,j,t;
r[0] = 0; //长度为0的钢条收益为0
for (i = 1; i <=n; i++){
q = -1; //初始为负数
for (j = 1; j <= i; j++){
t = price[j] + r[i - j]; //计算长度为i的钢条的切割方案收益
if (t > q){
q = t;
s[i] = j; //保存长度为i的钢条第一次切割的长度
}
}
r[i] = q; //计算完长度为i的钢条的造价收益保存
}
return q;
}