[算法]动态规划-钢条切割

钢条切割算法

某公司出售一段长度为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 钢条切割

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页