动态规划:钢条切割问题

问题描述

直接用《算法导论》上的原文。
在这里插入图片描述在这里插入图片描述

伪代码

自顶向下

在这里插入图片描述在这里插入图片描述

自底向上

在这里插入图片描述

代码

自顶向下

#include<iostream>
#include<iomanip>
#include<string.h>
using namespace std;
typedef struct Cut{
    int *r;
    int *s;
}cut;

cut memoized_cut_rod_aux(int *p,int n,int *r,int *s){
    cut ret={r,s};
    //最大收益
    int q;
    //如果表里面已经记录过切割这个长度的钢条的最大收益,那么久直接引用表中数据
    if(r[n]>=0)
        q=r[n];
    //切割长度为0的钢条,收益为0
    if(n==0)
        q=0;
    //否则需要计算切割这个长度的钢条的最大收益
    else{
        q=INT_MIN;
        //最大收益就是切的任意段的价值中最高的一个,左边切成i段,右边切成n-i段的价值的和
        for(int i=1;i<n+1;i++){
            //p[i]代表的是长度为I的钢条的单价
            int tem=p[i]+memoized_cut_rod_aux(p,n-i,r,s).r[n-i];
            if(q<tem){
                q=tem;
                s[n]=i;
            }
        }

        r[n]=q;
    }
    return ret;
}
cut memoized_cut_rod(int *p,int n){
    //先定义一个数组r,用于保存最优解,r[i]表示切割长度为i的钢条的最优解,显然,r[0]=0,r[]的长度应该为n+1,因为要让r[n]有意义,r[n]就是我们要的值。
    int *r=(int*)malloc((n+1)*sizeof(int));
    // 初始化r,让所有的切割收益为无穷小
    memset(r,INT_MIN,sizeof(r));
    //再定义一个数组s,用于保存最优的切割方法,
    int *s=(int*)malloc((n+1)*sizeof(int));
    memset(s,INT_MIN,sizeof(s));
    
    return memoized_cut_rod_aux(p,n,r,s);
}

int main(){
    int p[]={0,1,5,8,9,10,17,17,20,24,30};
    Cut cut=memoized_cut_rod(p,sizeof(p)/sizeof(int));
    for(int i=0;i<sizeof(p)/sizeof(int);i++){
        cout<<setw(4)<<i;
    }
    cout<<endl;
    for(int i=0;i<sizeof(p)/sizeof(int);i++){
        cout<<setw(4)<<cut.r[i];
    }
    cout<<endl;
    for(int i=0;i<sizeof(p)/sizeof(int);i++){
        cout<<setw(4)<<cut.s[i];
    }
    cout<<endl;
    return 0;
}

自底向上

#include<iostream>
#include<iomanip>
#include<string.h>
using namespace std;
//定义一个结构体去存储两个数组,一个是最优解的价值,另一个是切割方法,这里我们只存储将最左边切割成多长,因为右边部分是更小的子问题,也可以用最左边切割成多长来表示
typedef struct Cut{
    int *r;
    int *s;
}cut;

cut bottom_up_cut_rod(int *p,int n){
    //先定义一个数组r,用于保存最优解,r[i]表示切割长度为i的钢条的最优解,显然,r[0]=0,r[]的长度应该为n+1,因为要让r[n]有意义,r[n]就是我们要的值。
    int *r=(int*)malloc((n+1)*sizeof(int));
    // 初始化r,让所有的切割收益为无穷小
    memset(r,INT_MIN,sizeof(r));

    //再定义一个数组s,用于保存最优的切割方法,
    int *s=(int*)malloc((n+1)*sizeof(int));
    memset(s,INT_MIN,sizeof(s));
    cut ret={r,s};
    //最大收益
    int q;
    //现在采用自然增长模式对子问题进行求解,j代表着要求解的子问题的长度,它将从1开始到n,n即为我们要求解的最终问题
    for(int j=1;j<n+1;j++){
        q=INT_MIN;
        //求解模块为j的问题,那么也就是把长度为j的钢条分割的所有可能中最大的一种
        for(int i=0;i<=j;i++){
            // q=max(q,p[i]+r[j-i]);
            if(q<(p[i]+r[j-i])){
                q=p[i]+r[j-i];
                s[j]=i;
            }
        }
        r[j]=q;
    }
    return ret;   
}

int main(){
    int p[]={0,1,5,8,9,10,17,17,20,24,30};
    Cut cut=bottom_up_cut_rod(p,sizeof(p)/sizeof(int)-1);
    for(int i=0;i<sizeof(p)/sizeof(int);i++){
        cout<<setw(4)<<i;
    }
    cout<<endl;
    for(int i=0;i<sizeof(p)/sizeof(int);i++){
        cout<<setw(4)<<cut.r[i];
    }
    cout<<endl;
    for(int i=0;i<sizeof(p)/sizeof(int);i++){
        cout<<setw(4)<<cut.s[i];
    }
    cout<<endl;
    return 0;
}

运行结果

因为自顶向下和自底向上除了计算时候的路径不同,结果是一样的,所以只放一个即可 。
在这里插入图片描述这里附书中的运行结果,可以看到结果是一致的
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值