• 动态规划——钢条切割


    钢条切割问题:给定一段长度为n英寸的钢条和一个价格表pii=1,2,...,n).求切割钢条方案,使得销售收益rn最大。注意,如果长度为n英寸的钢条价格pn足够大,最优解可能就是完全不需要切割。 

     

    长度i

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    价格p

    1

    5

    8

    9

    10

    17

    17

    20

    24

    30

     

    n = 4

    方案              收益

    1+1+1+1     r = 4

    1+1+2          r = 7

    2+2               r = 10

    4                   r =9

     

    递归求解:采用朴素递归算法效率很低,需要反复求解相同的子问题。

    将钢条从左边切割长度为i+1的一段(i0开始),只对剩下的长度为n-(i+1)的一段进行切割(递归求解)。

    公式:rn  = maxpi+rn-i

    在此公式中,原问题的最优解只包含一个相关问题(右端剩余部分)的解,而不是两个。方法考察了所有的2n-1种可能的方案。Tn = 2n

     

    使用动态规划求解:仔细安排求解顺序,对每个问题只求解一次,并将结果保存下来。Tn= Θ(n2

     

    n  带备忘的自顶向下法(top-down with memoization).

    n  自底向上法(bottom-up method)。将子问题按规模排序,按由小到大的顺序进行求解。

     

    两种方法仅有的差异是在某些特殊情况下,自顶向下方法并未真正递归地考察所有可能的子问题。由于没有频繁的递归函数调用的开销,自底向上方法的时间复杂性函数通常具有更小的系数

     

     

    #include<iostream>
    #include<algorithm>
    usingnamespace std;
     
    int cutRod(int * p, int n);//递归迭代,低效版。复杂度指数级 2^n
    int m_TD(int *p, int n, int *r);//memoized_top-down 备忘-自顶向下  N^2
    int BD(int *p, int n);//button-up 自底向上法N^2
    int ** ext_BD(int * p, int n);//exetenden button-up 自底向上的扩展算法。保存切割方案,保留第一段钢条的最优切割长度
    void printSolution(int * p, int n);
     
    int main(){
        int r[100];//保存子问题的求解值。 
        int p[] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
        //int p[] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 ,32,42,52,55,56,58,60,63,66,70,80,100,110,120,130,140,150,160,170,180};
        int money = cutRod(p, 4);
        int m = m_TD(p, 4, r);
        printSolution(p, 11);//查看前10根需要输入11.
        system("pause");
        return 0;
    }
    int cutRod(int *p, intn){//n=钢条长度
        if (n == 0){
           return 0;
        }
        int maxValue = 0;
        for (int i = 0; i < n; i++)
        {
           int n2 = n - (i + 1);//拆分成i+1根,和n-(i+1)根, i = 0表示,拆分成1根和n-1根
           maxValue = max(maxValue, p[i] + cutRod(p, n2));
        }
       
        return maxValue;
    }
    //memoized_top-down 备忘-自顶向下
    int m_TD(int *p,intn,int *r){//p = price保存价钱的数组,n = num钢条的长度,r保存收益的数组,即保存子问题的求解值,初始化为所有元素都为0。
        /* n = 0
           n = 1
               i = 0, n2 = 0,第一根为1,第二根为0
               q = max(0,p[0]+m_TD(p,0,r)) = 1;
               r[1] = 1;
     
           n = 2
               i = 0, n2 = 1,第一根为1,第二根为1
               q = max(0,p[0]+m_TD(p,1,r)) = max(0,1+1) = 2 ;
     
               i = 1, n2 = 0 ,第一根为2,第二根为0
               q = max(2,p[1]+m_TD(p,0,r)) = max(5,2+0) = 5;
        */
        int q = 0;
        if (r[n] >= 0){//判断是否已经有解。
           returnr[n];
        }
        if (n == 0)   return 0;
        else
        {
           int n2 = 0;
           for (int i = 0; i < n; i++)
           {
               n2 = n - (i + 1);
               q = max(q, p[i] + m_TD(p, n2,r));
           }
        }
        r[n] = q;
        return q;
    }
     
    //button-up 自底向上法
    int BD(int *p, intn){
       
        //n =2,依此求得r[1]、r[2];
        /*i = 1,
        *   j = 1, q = max(0,p[1-0] + r[0]) =  1;
        *   r[1] = 1;
        */
     
        /*i = 2,
        *   j = 1, q = max(1,p[0] + r[1]) = 2;切分成2 = 1+1
        *   j = 2  q = max(5,p[1] + r[0]) = 5;  切分成2 = 2+0;
        *   r[2] = 5;
        */
        int r[100] = { 0 };//求解每个子问题i=1 -> n根钢条的问题解,求得解保存如r数组中。
        for (int i = 1; i <=  n; i++)
        {
           int maxValue = 0;
           int j = 1;
          
           for ( j; j <=i; j++)
           {
               maxValue = max(maxValue, p[j - 1] + r[i - j]);
           }
           r[i] = maxValue;
        }
        return r[n];
        //尝试着将i j初始化0,但是没有找到对应的方法.还是按书上的初始化1.
    }
     
    //exetenden button-up 自底向上的扩展算法。保存切割方案,保留第一段钢条的最优切割长度
    int ** ext_BD(int * p, intn){
     
        staticint s[50] = { 0 };
        staticint r[50] = { 0 };//求解每个子问题i=1 -> n根钢条的问题解,求得解保存如r数组中。
        staticint *result[2] = { r, s };//result保存两个数组指针。
       
        for (int i = 1; i <= n; i++)
        {
           int maxValue = 0;
           int j = 1;
           for (j; j <= i; j++)
           {
               int value = p[j - 1] + r[i - j];
               if (maxValue < value){
                  maxValue = value;
                  s[i] = j;//保留第一段钢条的最优切割长度
                  r[i] = maxValue;
               }
           }
        }
        return result;
    }
    void printSolution(int * p,intn){
       
        int ** sol = ext_BD(p, n);
        int * r = sol[0];
        int * s = sol[1];
     
        for (int i = 0; i < n; i++)
        {
           cout << i << "    r:" << r[i] << "      first_cut: "<<s[i]<<endl;
        }
       
    }

     参考文献殷建平等译.算法导论(第三版).机械工业出版社

     

  • 相关阅读:
    JavaBean 之Hello World(入门实例)
    Velocity之Hello World(入门实例)
    URL, URI 和 URN 之间的区别
    Windows平台的Windbg/x64dbg/OllyDbg调试器简介以及符号文件*.pdb总结(★firecat推荐★)
    VC++内存泄漏检测方法(5):使用强大的Windbg工具,重点是Symbols Path设置
    安装与配置windbg 的符号文件(转自一片文章的回复,回复者RegKiller)
    WinDbg下载符号文件(内核层、用户层)
    Windbg下载微软符号表
    windbg符号表问题
    WinDBG加载符号表的一点心得体会
  • 原文地址:https://www.cnblogs.com/dingblog/p/4465267.html
Copyright © 2020-2023  润新知