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

钢条切割问题

现有一段长度为n英寸的钢条和一个价格表pi,求切割方案使销售利益最大rn最大

长度为n英寸的钢条共有2n?1种不同的切割方案,因为可以每个整英寸的位置都可以决定切割或者不切割。

  • 为了得到rn最大,可以把这个问题分成子问题求解,先切一刀,再考虑余下的部分的最大收益即求

    rn=max{pk+rn?k}(k=1,2,3…n-1),

    pk部分不进行继续切割,直接作为一个整体售出 ;

    rn?k部分继续切割,考虑所有的情况,分成子问题。

    求出所有k值对应的收益最大者作为rn

  • 也有可能不进行任何切割利益最大即rn=pn

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

动态规划求解

考虑上面的算法实现:rn=max(pi+rn?i) (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\n",Memoized_Cut_Rod(p,i));
     printf("尺寸为%d的最大收益:%d ",i,Extended_Bottom_Up_Cut_Rod(p,i));
    // printf("%d  \n",s[i]);
     printf("切割方法:");
     Print_Cut_Rod_Solution(p,i);
     free(s);
     s=NULL;
     printf("\n");
   }
}

时间: 2024-10-29 19:09:45

算法导论--动态规划(钢条切割)的相关文章

算法导论-动态规划-钢条切割

动态规划通常用于解决最优化问题,在这类问题中,通过做出一组选择来达到最优解.在做出每个选择的同时,通常会生成与原问题形式相同的子问题.当多于一个选择子集都生成相同的子问题时,动态规划技术通常就会很有效,其关键技术就是对每个这样的子问题都保存其解,当其重复出现时即可避免重复求解. 钢条切割问题 Serling公司购买长钢条,将其切割为短钢条出售.切割工序本身没有成本支出.公司管理层希望知道最佳的切割方案.假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元

算法导论 动态规划 钢条切割问题的自底向上解法

正式应用动态规划. 适用于动态规划解决的问题应拥有以下两个要素: 1. 最优子结构(最佳选择) 2.子问题重叠(最终的最优解的每个分部步骤,都是当前最优的子解.与贪心算法试图通过局部最优解来组合成最优解的思想相似) 下面第一版代码中,依旧存在与上一篇第一版代码相同的问题--只能求解p数组中给出的最大限度.N>=10,代码就不能够求解出正确答案.(代码中你们都懂的- -卖个萌(O(∩_∩)O哈哈~) 主要用第一版代码记录思路. 共给出两个功能函数. Max()和Bottom_Cut_Rod(),作

算法导论--动态规划(矩阵链乘法)

矩阵链乘法问题 给定一个n个矩阵的序列?A1,A2,A3...An?,我们要计算他们的乘积:A1A2A3...An.因为矩阵乘法满足结合律,加括号不会影响结果.可是不同的加括号方法.算法复杂度有非常大的区别: 考虑矩阵链:?A1,A2,A3?.三个矩阵规模分别为10×100.100×5.5×50 假设按((A1A2)A3)方式,须要做10?100?5=5000次,再与A3相乘,又须要10?5?50=2500,共须要7500次运算: 假设按(A1(A2A3))方式计算.共须要100?5?50+10

算法导论--动态规划(最长公共子序列)

最长公共子序列问题(LCS) 给定两个序列X=?x1,x2,x3...xm?和Y=?y1,y2,y3...xn?,求X和Y的最长公共子序列. 例如:X=?A,B,C,B,D,A,B?,和Y=?B,D,C,A,B,A?,的最长公共子序列为?B,C,B,A?,长度为4: 对于此问题,可以采用暴力求解的方式来比对,即穷举出X的所有子序列,用每个子序列与y做一 一比较.假如X序列共有m个元素,对每个元素可以决定选或不选,则X的子序列个数共有2m个,可见与长度m呈指数阶,这种方法效率会很低. 动态规划 前

算法导论--动态规划(装配线调度)

装配线问题: 某个工厂生产一种产品,有两种装配线选择,每条装配线都有n个装配站.可以单独用,装配线1或2加工生产,也可以使用装配线i的第j个装配站后,进入另一个装配线的第j+1个装配站继续生产.现想找出通过工厂装配线的最快方法. 装配线i的第j个装配站表示为Si,j,在该站的装配时间是ai,j 如果从 Si,j装配站生产后,转移到另一个生产线继续生产所耗费的时间为ti,j 进入装配线花费时间ei,完成生产后离开装配线所耗费时间为xi 令f*表示通过生产所有路线中的最快的时间 令fi[j]表示从入

动态规划 钢条切割问题的朴素解法

第一版代码 : #include <iostream> using namespace std; int max(int a,int b) { if(a>=b)return a; else return b; } int cut_rod(int *p,int n) { int q=NULL; if(n==0)return 0; else for(int i=0;i<n;i++) { q=max(q,p[i]+cut_rod(p,n-1-i)); } return q; } int

算法导论动态规划 15.1-3 钢条切割问题

算法导论的第一个动态规划问题--钢条切割 我们知道钢条的长度和价格为: 长度i 1 2 3 4 5 6 7 8 9 10 价格pi 1 5 8 9 10 17 17 20 24 30 书上的两种方法已经很清楚,这里主要说一下课后练习里面15-3钢条成本加上切割成本,即要切割的次数最少.15-4返回切割方案 #include<fstream> #include<iostream> using namespace std; int main() { int const N = 11;

算法导论---------动态规划之钢条切割

动态规划方法通常用来求解最优化问题.动态规划算法设计步骤: 1.刻画一个最优解的结构特征. 2.递归定义最优解的值. 3.计算最优解的值,通常采用自底向上的方法. 4.利用计算出的信息构造一个最优解. 动态规划的实现方法: 带备忘的自顶向下法:此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中).当需要一个子问题的解时,过程首先检查是否已经保存过此解.如果是,则直接返回保存的值,从而节省了计算时间:否则,按通常方式计算这个子问题. 自底向上法:这种方法一般

动态规划 钢条切割问题

#include <stdio.h> /* *钢条切割问题: *问题描述 假设公司出售一段长度为i英寸的钢条的价格为Pi(i = 1, 2, ...单位:美元),下面给出了价格表样例: 长度i 1 2 3 4 5 6 7 8 9 10 价格Pi 1 5 8 9 10 17 17 20 24 30 切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大. */ //假定价格一开始已经给出了,用全局的写死 #define max(x,y) (x)>