问题
钢条切割问题是这样的: 给定⼀段长度为n的钢条和⼀个价格表pi(i=1,2,…n), 求切割钢条⽅案, 使得销售收益最⼤。
注意, 如果长度为n英⼨的钢条的价格pn⾜够⼤, 最优解可能就是完全不需要切割。
输入:钢条的长度n,不同长度钢条的价值Pi,{i=1,…,n}
输出:1、最大收益,2、切割方案。、
输入输出格式如下:
请先在工程目录里建立input.txt文档,因为程序使用txt文档输入来接收数据
输入:
10
1 2 3 4 5 6 7 8 9 10
10
1 1
2 5
3 8
4 9
5 10
6 17
7 17
8 20
9 24
10 24
输入第一行代表输入多少个钢条的数据
第二行是各个钢条长度
第3行代表有多少种钢条单价价格
之后以下各行是对应单价,左列是序号,右列是单价
输出:
代码
#include<iostream>
#include<fstream>
#include<string.h>
using namespace std;
#define LEN 100//最大可计算长度
int C[LEN] = { 0 };
int p[LEN] = { 0 };
int cuts[LEN] = { 0 };
int n[LEN] = {0};//输入的钢条长度
int main()
{
int i, maxk=0;//k记录最大长度
void fun(int n);
void printCutSolution( int length);
int num;//输入多少条钢条长度的数据
int m;//价格种类
ifstream fp_read("input.txt");
ofstream fp_write("output.txt");
if (fp_read.fail() || fp_write.fail())
{
cout << "错误!" << endl;
exit(0);
}
fp_read >> num;
for (i = 1; i <= num; i++)
{
fp_read >> n[i];
if (n[i] > maxk)
maxk = n[i]; //后面计算要按最大长度的钢管扩展C[i];
}
fp_read >> m; //有多少种价格
for (i = 1; i <= m; i++)
{
fp_read >> maxk; //接收价格序号,无用
fp_read >> p[i];
}
fun(maxk);
for (i = 1; i <= num; i++)
{
fp_write <<C[n[i]] << endl;
cout<< C[n[i]] << endl;
printCutSolution(n[i]);// 输出切割方案
}
return 0;
}
void fun(int n)
{
int i, j,temp;
for (j = 1; j <= n; j++)
{
temp = 0;
for (i = 1; i <= j; i++)
if (temp < C[i] + C[j - i])
{
temp = C[i] + C[j - i];
cuts[j] = i;
}
C[j] = (temp > p[j]) ? temp : p[j];
if (C[j] == p[j])
cuts[j] = j;
}
}
void printCutSolution(int length)
{
if (length <= 0)
return;
cout << "切割方案: ";
while (length > 0)
{
cout << cuts[length] << " ";
length -= cuts[length];
}
cout << endl;
}
代码分析
设C[j]为长度为j的钢管能切割成所能达到的最大收益,p[j]为长度为j的钢管的单价,容易得出递推公式C[j]=max{p[j],p[i]+C[j-i]}, 采用自底向上的方法,依次从1开始计算出每个长度的钢管的最大收益,即C[j],有2种选择,第一种,它等于当前长度j的单价,即单价就是最大收益,第二种,或者等于切割成i和j-i两部分,等于长度为i的钢管单价加上长度为j-i的钢管的最大收益,这2种选择求最大值即可,显然可以据此推出最优解。
void fun(int n)
{
int i, j,temp;
for (j = 1; j <= n; j++)
{
temp = 0;
for (i = 1; i <= j; i++)
if (temp < C[i] + C[j - i])
{
temp = C[i] + C[j - i];
cuts[j] = i;
}
C[j] = (temp > p[j]) ? temp : p[j];
if (C[j] == p[j])
cuts[j] = j;
}
}
在考虑输出最优方案时,在函数fun中,我添加了一个数组cuts,用于保存每个长度对应的切割点位置。在内层循环中,当找到更大的收益时,更新temp、C[j]和cuts[j]的值。
if (C[j] == p[j])
cuts[j] = j;
看上面2行代码,值得注意的是,如果长度为n英⼨的钢条的价格p[n]⾜够⼤, 最优解可能就是完全不需要切割。故用cuts[]数组记录时,如果j长度的钢条最大收益等于其本身的j长度钢管单价时,将cuts[]更新为该长度j
void printCutSolution(int length)
{
if (length <= 0)
return;
cout << "切割方案: ";
while (length > 0)
{
cout << cuts[length] << " ";
length -= cuts[length];
}
cout << endl;
}
函数printCutSolution接受切割点数组cuts[]和钢条长度length作为参数。它通过回溯方式,根据切割点数组输出切割方案。从长度length开始,不断追溯切割点并输出,直到切割点为0。这样就可以输出最大收益对应的切割方案。
时间复杂度分析
时间复杂度取决于两个嵌套循环的迭代次数。
在 fun 函数中,外层循环迭代次数为 n,内层循环迭代次数取决于当前钢条长度 j,最多为 j 次。因此,fun 函数的时间复杂度为 O(n^2)。
在 printCutSolution 函数中,根据切割方案输出长度和切割点,循环迭代的次数最多为 length 次。因此,printCutSolution 函数的时间复杂度为 O(length)。
综上所述,整个代码的时间复杂度主要由 fun 函数的时间复杂度决定,为 O(n^2)。