• 算法导论--动态规划(钢条分割)


    钢条分割

    现有的长度n寸钢筋和价格表pi,求分割方案使销售利益最大rn最大
    这里写图片描写叙述

    长度为n英寸的钢条共同拥有2n1种不同的分割方案。由于能够每一个整英寸的位置都能够决定分割或者不分割。

    • 为了得到rn最大,能够把这个问题分成子问题求解,先切一刀,再考虑余下的部分的最大收益即求
      rn=max{pk+rnk}(k=1,2,3…n-1),
      pk部分不进行继续分割。直接作为一个总体售出 ;
      rnk部分继续分割,考虑全部的情况,分成子问题。
      求出全部k值相应的收益最大者作为rn

    • 也有可能不进行不论什么分割利益最大即rn=pn

      综上rn=max(pi+rni) (i=1,2,3…n , r0=0)

    动态规划求解

    考虑上面的算法实现:rn=max(pi+rni) (i=1,2,3…n , r0=0),为了计算出rn必须计算出全部情况的分割方法。即有n个分支。第i个分支又相应着i个子分支。所以若採用递归调用计算全部情况,调用次数将是2n次。执行时间为n的指数函数。

    上面的分析知。当中反复计算了非常多次子问题,假设有个好的计算顺序。子问题先计算一次。把结果保存下来,下次再遇到这个子问题,仅仅须要查表就可以。动态规划方法是付出额外的内存空间来节省计算时间。

    带备忘的自顶向下法

    仍採用递归的方法,但过程中保存已求解过子问题的解。当须要一个子问题的解时。首先检查是否已经保存过此解。

    int Memoized_Cut_Rod(int *p,int n)
    {   
        int i=0;
        int * r = (int *)malloc((n+1)*sizeof(int));  //分配空间,用来存储已经计算过的子问题
        for (i=0;i<=n;i++)                          //初始化
            r[i] = -1;
        return Memoized_Cut_Rod_Aux(p,n,r);         
    }
    int Memoized_Cut_Rod_Aux(int *p,int n,int *r)
    {
        int q= -1,i;
        if (r[n] >=0)   //首先检查是否已经计算过,若有,直接返回
            return r[n];
        if (n == 0)     //钢条长度为0。收益为0
            q=0;
        else
        {
            q = -1;
            for (i=1;i<=n;i++) 
            {
               q=Max(q,p[i]+Memoized_Cut_Rod_Aux(p,n-i,r));   //自顶向下递归
            }
            r[n] = q;        //保存子问题的值
        }
    
        return q;        //返回最大的收益
    }

    自底向上法

    把子问题按规模排序,按由小到大的顺序进行求解。当求解某个问题时,它所依赖的子问题已经被计算过,结果已经保存,这样每一个子问题仅仅须要计算一次。

    int Bottom_Up_Cut_Rod(int *p,int n)
    {
    int * r = (int *)malloc((n+1)*sizeof(int));   //分配空间。用来存储已经计算过的子问题
    int i,j,q=-1;
      r[0]=0;
      for (j=1;j<=n;j++)
        {
           q=-1;
           for(i=1;i<=j;i++)
           {
               q=Max(q,p[i]+r[j-i]);   //自底向上。由小到大求解子问题
           }
           r[j]=q;   //保存求解后的结果
        }
      return r[n];
    }

    重构解

    上面的代码是返回的最大收益。并没有返回详细分割方法。以下代码保存了每种尺寸,相应的左边部分大小(即作为总体售出部分)

    int Extended_Bottom_Up_Cut_Rod(int *p,int n)
    {
        int * r = (int *)malloc((n+1)*sizeof(int));   
        int i,j,q=-1;
        s = (int *)malloc((n+1)*sizeof(int));  //保存每次分割前左边部分剩余尺寸
        s[0]=0;
        r[0]=0;
        for (j=1;j<=n;j++)
        {
            q=-1;
            for(i=1;i<=j;i++)
            {
    
                if (p[i]+r[j-i] > q)
                {
                    s[j] = i;      //保存左边部分(作为总体售出的部分)
                    q = p[i] + r[j-i];
                }
            }
            r[j]=q;
        }
    
        return r[n];
    }

    输入钢条的详细分割方法:

    void Print_Cut_Rod_Solution(int *p,int n)
    {
        while(n>0)
        {
            printf("%d   ",s[n]);   //循环输出尺寸为n的钢条分割方法
            n=n-s[n];
        }
    }

    例程

    /************************************************************************
    CSDN 勿在浮沙筑高台 
    http://blog.csdn.net/luoshixian099
    算法导论--动态规划(钢条分割)
    2015年6月2日                    
    ************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    int *s=NULL;
    int Memoized_Cut_Rod_Aux(int *p,int n,int *r);
    int Max(int a,int b)
    {
        return a>b?a:b;
    }
    int Memoized_Cut_Rod(int *p,int n)
    {   
        int i=0;
        int * r = (int *)malloc((n+1)*sizeof(int));  //分配空间,用来存储已经计算过的子问题
        for (i=0;i<=n;i++)                          //初始化
            r[i] = -1;
        return Memoized_Cut_Rod_Aux(p,n,r);         
    }
    int Memoized_Cut_Rod_Aux(int *p,int n,int *r)
    {
        int q= -1,i;
        if (r[n] >=0)   //首先检查是否已经计算过,若有,直接返回
            return r[n];
        if (n == 0)     //钢条长度为0,收益为0
            q=0;
        else
        {
            q = -1;
            for (i=1;i<=n;i++)
            {
               q=Max(q,p[i]+Memoized_Cut_Rod_Aux(p,n-i,r));
            }
            r[n] = q;        //保存子问题的值
        }
    
        return q;        //返回最大的收益
    }
    int Bottom_Up_Cut_Rod(int *p,int n)
    {
    int * r = (int *)malloc((n+1)*sizeof(int));   //分配空间。用来存储已经计算过的子问题
    int i,j,q=-1;
      r[0]=0;
      for (j=1;j<=n;j++)
        {
           q=-1;
           for(i=1;i<=j;i++)
           {
               q=Max(q,p[i]+r[j-i]);   //自底向上,由小到大求解子问题
           }
           r[j]=q;   //保存求解后的结果
        }
      return r[n];
    }
    
    int Extended_Bottom_Up_Cut_Rod(int *p,int n)
    {
        int * r = (int *)malloc((n+1)*sizeof(int));   
        int i,j,q=-1;
        s = (int *)malloc((n+1)*sizeof(int));  //保存每次分割前左边部分剩余尺寸
        s[0]=0;
        r[0]=0;
        for (j=1;j<=n;j++)
        {
            q=-1;
            for(i=1;i<=j;i++)
            {
    
                if (p[i]+r[j-i] > q)
                {
                    s[j] = i;      //保存左边部分(作为总体售出的部分)
                    q = p[i] + r[j-i];
                }
            }
            r[j]=q;
        }
    
        return r[n];
    }
    void Print_Cut_Rod_Solution(int *p,int n)
    {
    
        while(n>0)
        {
            printf("%d   ",s[n]);   //循环输出尺寸为n的钢条分割方法
            n=n-s[n];
        }
    }
    void main()
    {
       int p[]={0,1,5,8,9,10,17,17,20,24,30};
       int i;
       for (i=1;i<=10;i++)
       {
         //printf("%d
    ",Memoized_Cut_Rod(p,i));
         printf("尺寸为%d的最大收益:%d ",i,Extended_Bottom_Up_Cut_Rod(p,i));
        // printf("%d  
    ",s[i]);
         printf("分割方法:");
         Print_Cut_Rod_Solution(p,i);
         free(s);
         s=NULL;
         printf("
    ");
       }
    }
    

    这里写图片描写叙述

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    IOS8定位
    ios通讯录基本操作2014-12月版
    ios悬浮按钮的实现
    MartinLiPageScrollView广告栏实现
    ios分享(友盟分享)
    vue2.0路由-适合刚接触新手简单理解
    git链接GitHub命令及基本操作
    Node
    JS数组sort()排序
    原生JS获取CSS样式并修改
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4829004.html
Copyright © 2020-2023  润新知