算法学习_动态规划_Rod cutting

问题描述:已知各个长度的钢条对应的价格;长度为1、2、3、4、5、6、7、8、9、10,对应的价格分别为1、5、8、9、10、17、17、20、24、30;现有一段钢条的长度为n(0<n<11),经过怎样的划分使得它的总价值最大,求出最大价值。

分析:设长度为n的钢条,总价值最大为r(n)=max(p[i]+r(n-i)),其中1<=i<=n;

1、采用Recursive top-down 方法实现如下:

#include<iostream>
using namespace std;
int cut_rod(int (&p)[11],int n){
    if(n==0)
        return 0;
    int q=-99999;
    for(int i=1;i<=n;i++){
        int p_rest=cut_rod(p,n-i);
        q=(q>p[i]+p_rest?q:p[i]+p_rest);
    }
    return q;
}
int main(){
    int p[11]={0,1,5,8,9,10,17,17,20,24,30};
    cout<<cut_rod(p,7)<<endl;
    return 0;
}

上面这个算法,有很多次重复的递归。比如计算n=4情况,有(p[4]+0), (p[3]+r(1)), (p[2]+r(2)), (p[1]+r(3))四种情况求最大值,需要计算r(3)、r(2)、r(1);这时求(p[1]+r(3))时,需要计算r(3),就又要重复计算(p[1]+r(2)), (p[2]+r(1)), (p[3]+0),即重复计算了r(2)、r(1)。时间复杂度为2^n。
2、采用动态规划的方法

(1)top-down with memorization:

#include<iostream>
using namespace std;
int memorized_cut_rod_aux(int (&p)[11],int n,int (&r)[11]){
    if(r[n]>=0)
        return r[n];
    int q;
    if(n==0)
        q=0;
    else {
        q=-99999;
        for(int i=1;i<=n;i++){
            int p_rest=memorized_cut_rod_aux(p,n-i,r);
            q=(q>p[i]+p_rest?q:p[i]+p_rest);
        }
    }
    r[n]=q;
    return q;
}
int main(){
    int p[11]={0,1,5,8,9,10,17,17,20,24,30};
    int r[11];
    for(int i=0;i<11;i++)
        r[i]=-99999;
    cout<<memorized_cut_rod_aux(p,7,r)<<endl;
    return 0;
}

 

比如计算r(4),会调用p[1]+r(3), r(3)调用p[1]+r(2), r(2)调用p[1]+r(1)。递归到此,长度n为1,memorized_cut_rod_aux(p,1,r), 会调用最后一个递归memorized_cut_rod_aux(p,0,r),此时n为0,q=0,不再递归调用了,把q=0赋给r[0],递归第一次返回时有r[0]>=0,返回r[0],p[1]+r[0]得到r(1)的最大值、、、总之memorized_cut_rod_aux(p,n,r)的调用顺序是n从n、n-1、n-2、n-3、、、、,则递归到n=0返回时,会逆着记录r[0]、r[1]、

r[2]、、、的值,并使下一次递归的真正执行调用数组r前面已经计算出的所有元素值。

(2)bottom-up method:

#include<iostream>
using namespace std;
int bottom_up_cut_rod(int (&p)[11],int n){
    int r[11];
    r[0]=0;
    int q;
    for(int i=1;i<=n;i++){
        q=-99999;
        for(int j=1;j<=i;j++)
            q=(q>p[j]+r[i-j]?q:p[j]+r[i-j]);
        r[i]=q;
    }
    return r[n];
}
int main(){
    int p[11]={0,1,5,8,9,10,17,17,20,24,30};
    cout<<bottom_up_cut_rod(p,7)<<endl;
    return 0;
}

比如现在求n=4,从1、2、3、4遍历,遍历1时,计算p[1]+r[0]值,即为r[1]。记录之后,遍历2,计算p[1]+r[1]和p[2]+r[0],比较出最大值记录为r[2]。记录之后再遍历3,计算p[1]+r[2],p[2]+r[1],

p[3]+r[0],比较出最大值记录为r[3]。同样可以计算出r[4],返回。

posted @ 2013-07-22 15:09  开心成长  阅读(562)  评论(0编辑  收藏  举报