钢条切割算法
某公司出售一段长度为i英寸的钢条的价格为p(i)如一次为{1,5,8,9,10,17,17,20,24,29},求给出一段长度为n的钢条,怎么切才能得到最佳收益?
对于这个问题,可以先切为两段,然后取最好的情况,第一段长度为i,则第二段为n-i,那么情况就有 i=1..n 共n种情况.
for(i=1;i<=n;i++)
{
q= maxmax(q, p(i)+cut_rod(n-i));
}
切除了i长,那么还剩n-i长,只需求出对n-i长的钢条如何切割,可以递归求解.
用递归算法来求解,时间复杂度为n^2,损耗较大
于是做了改进,将每次计算的结果保存下来,每次切割时如果已经有结果则不再计算.
int memorized_cut_rod_aux(int n,int * r)
{
int i;
int q=NEG_INF; //设为负无穷
if (r[n]>=0) return r[n];
if (n==0) return 0;
for(i=1;i<=n;i++)
{
q= maxmax(q, p(i)+cut_rod(n-i));
}
r[n]=q;
return q;
}
int memoized_cut_rod(int n)
{
int * r=(int*)malloc(sizeof(int)*(n+1));
int i;
int q=NEG_INF;
for(i=0;i<=n;i++)
{
r[i]=NEG_INF;
}
q=memorized_cut_rod_aux(n,r);
free(r);
return q;
}
上面的代码对递归算法做了改进,但是递归栈的调用损耗也比较大.
所以有进一步改进,所以有了从底向上法.由于n的切割必然需要n-1的切割,所以从1开始计算,并且每次计算时把结果保存起来.
时间复杂度为多项式级别
int bottom_up_cut_rod(n){
int * r=(int*)malloc(sizeof(int)*(n+1));
int i,j,q;
r[0]=0;
for(j=1;j<=n;j++)
{
q=NEG_INF;
for(i=1;i<=j;i++)
{
q=maxmax(q,p(i)+r[j-i]);
}
r[j]=q;
}
return r[n];
}
再一次改进,加入了存储第一次切割的长度的功能,可以求出长度为n的钢条切割的切割路径.
`
//拓展的从底向上完成,可以存储第一次切割的长度
int extended_bottom_up_cut_rod(n){
int * r=(int*)malloc(sizeof(int)*(n+1));
int * s=(int*)malloc(sizeof(int)*(n+1));
int i,j,q,n2;
r[0]=0;
for(j=1;j<=n;j++)
{
q=NEG_INF;
for(i=1;i<=j;i++)
{
if(q<p(i)+r[j-i]){
q=p(i)+r[j-i];
s[j]=i;
}
}
r[j]=q;
}
n2=n;
printf("\n");
while(n2>0){
printf("%d",s[n2]);
n2-=s[n2];
if(n2>0) printf("->");
}
printf("\n");
return r[n];
}
每次调用可以把路径打印输出
贴上完整代码
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#define NEG_INF INT_MIN
#define MAXLEN 30
int s[MAXLEN+1];
int maxmax(int x,int y)
{
return x>y?x:y;
}
int p(int n)
{
const int p10[11]={0,1,5,8,9,10,17,17,20,24,29};
if(n<0) {
printf("error");
exit(-1);
}
return n/10*p10[10]+p10[n%10];
}
//递归切割钢条
int cut_rod(int n)
{
int i;
int q=NEG_INF;
if (n==0) return 0;
if (s[n]>0){
q=s[n];
}else{
for(i=1;i<=n;i++)
{
q= maxmax(q, p(i)+cut_rod(n-i));
}
s[n]=q;
}
return q;
}
//递归切割,并存储结果
int memorized_cut_rod_aux(int n,int * r)
{
int i;
int q=NEG_INF;
if (r[n]>=0) return r[n];
if (n==0) return 0;
for(i=1;i<=n;i++)
{
q= maxmax(q, p(i)+cut_rod(n-i));
}
r[n]=q;
return q;
}
int memoized_cut_rod(int n){
int * r=(int*)malloc(sizeof(int)*(n+1));
int i;
int q=NEG_INF;
for(i=0;i<=n;i++)
{
r[i]=NEG_INF;
}
q=memorized_cut_rod_aux(n,r);
free(r);
return q;
}
//从底向上完成
int bottom_up_cut_rod(n){
int * r=(int*)malloc(sizeof(int)*(n+1));
int i,j,q;
r[0]=0;
for(j=1;j<=n;j++)
{
q=NEG_INF;
for(i=1;i<=j;i++)
{
q=maxmax(q,p(i)+r[j-i]);
}
r[j]=q;
}
return r[n];
}
//拓展的从底向上完成,可以存储第一次切割的长度
int extended_bottom_up_cut_rod(n){
int * r=(int*)malloc(sizeof(int)*(n+1));
int * s=(int*)malloc(sizeof(int)*(n+1));
int i,j,q,n2;
r[0]=0;
for(j=1;j<=n;j++)
{
q=NEG_INF;
for(i=1;i<=j;i++)
{
if(q<p(i)+r[j-i]){
q=p(i)+r[j-i];
s[j]=i;
}
}
r[j]=q;
}
n2=n;
printf("\n");
while(n2>0){
printf("%d",s[n2]);
n2-=s[n2];
if(n2>0) printf("->");
}
printf("\n");
return r[n];
}
int main()
{
int i;
for (i=0;i<=MAXLEN;i++){
s[i]=NEG_INF;
}
for (i=1;i<=MAXLEN;i++){
//printf("%d\t%d\n",i,cut_rod(i));
printf("%d\t%d\n",i,memoized_cut_rod(i));
printf("%d\t%d\n",i,bottom_up_cut_rod(i));
printf("%d\t%d\n",i,extended_bottom_up_cut_rod(i));
}
}
<算法导论> 第十五章 动态规划 15.1 钢条切割