动态规划之钢条 切割

来源:互联网 发布:网络电话软件下载 编辑:程序博客网 时间:2024/05/17 06:23
  1. 问题描述
    假设有长度为n寸的钢管,我们可以切割这段钢管以获得最大的利益(假设切割钢管无花费),价格表pi已知,问如何切割才能是销售收益rn最大。
    钢条价格表
  2. 解决方案
    对于这个问题我们可以使用递归的方式来解决。
    public static int CUT_ROD(int[] p, int n){        if(n == 0){            return 0;        }        int q = -1;        for(int i = 1; i <= n; i++){            q = Math.max(q, p[i] + CUT_ROD(p, n-i));        }        return q;    }

完成首次切割后,我们将两段钢条看成是两个独立的钢条切割问题,我们通过组合两个相关子问题的最优解来选取组合收益最大者。
虽然递归的方法可以求解该问题,但是仔细分析我们发现递归过程中我们求解的某些子问题是相同的,规模也是一样的。我们可以在求解的过程中将子问题的解保留下来,以便于下次碰到相同的子问题可以直接使用。

public static int MEMOIZED_CUT_ROD(int[] p, int n){        int[] r = new int[n + 1];        for(int i = 0; i < r.length; i++){            r[i] = -1;        }        return MEMOIZED_CUT_ROD_AUX(p, n, r);    }    public static int MEMOIZED_CUT_ROD_AUX(int[] p, int n, int[] r){        if(r[n] >= 0)            return r[n];        int q = -1;        if(n == 0)            q = 0;        else {            for(int i = 1; i <= n; i++){                q = Math.max(q, p[i] + MEMOIZED_CUT_ROD_AUX(p, n-i, r));            }        }        r[n] = q;        return q;    }

上面这种方法称作带备忘的自顶向下发,此方法仍自然的递归形式编写过程,但过程会保存每个子问题的解,当需要一个子问题的解时,过程首先检查是否已经保存过此解。

public static int BOTTOM_UP_CUT_ROD(int[] p, int n){        int[] r = new int[n + 1];        r[0] = 0;        for(int i = 1; i <= n; i++){            int q = -1;            for(int j = 1; j <=i; j++){                q = Math.max(q, p[j] + r[i-j]);            }            r[i] = q;        }        return r[n];    }

上面的方法称为自低向上法,该方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的”子问题的求解。

  1. 总结
    动态规划个递归十分相似,动态规划的子问题是可以重复的,递归的子问题一般是规模逐渐减小的。
0 0