zoukankan      html  css  js  c++  java
  • 动态规划 -- 钢条切割

    /*
        动态规划和分治法相似,都是通过组合子问题的解来求解原问题。 但分治法是将
    问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来,求出
    原问题的解。与之相反,动态规划应用于子问题重叠的情况,即不同的子问题具有公共的
    子问题。在这种情况下,分治法会做很多不必要的工作。
        动态规划方法通常用来求解最优化问题,这类问题通常有很多可行解。我们希望寻找
    具有最优值的解。
        我们通常按照如下4个步骤来设计一个动态规划算法:
        · 刻画一个最优解的结构特征
        · 递归地定义最优解的值
        · 计算最优解的值,通常采用自底向上的方法
        · 利用计算出的信息构造一个最优解
    
    钢条切割:给出一个钢条长度的价格表,根据价格表,给定一段长度为 n 的钢条,求一个
    切割方案,使得收益最大。可能出现的一种特殊情况就是完全不需要切割。
    */
    
    #include <iostream>
    
    using namespace std;
    
    double max(double a, double b) {
        return a >= b ? a : b;
    }
    
    /*
    方法一:自顶向下递归实现:伪代码
        CUT-ROD(p, n)
            if n == 0
                return 0  // 如果长度为 0,那么收益肯定为 0
            q = -∞
            for i = 1 to n
                q = max(q, p[i] + CUT-ROD(p, n - i))
            自底向上版本
            return q
        这种实现方法中,CUT-ROD 反复用参数值对自身进行递归调用,即它反复求解了
    相同的子问题,那么在递归调用的时候,程序的工作量就会爆炸性地增长。复杂度为 2^n
    */
    
    double cutRod(double *table, int length) {
        if (length == 0)
            return 0;
        double q = 0;
        for (int i = 1; i <= length; ++i) {
            q = max(q, table[i] + cutRod(table, length - i));
        }
        return q;
    }
    
    /*
    使用动态规划方法求解
        上面的算法称为朴素递归算法,它的效率之所以很低,就是因为重复求解相同的子问题,
    因此动态规划要求仔细安排求解顺序,使得每个子问题只求解一次。那么着就需要保存结果,
    在再次用到这个结果的时候就不需要重新计算而是使用查找。
        当然,这样的话就需要使用额外的空间来保存计算的结果。但是一般来说这样的代价是值得的。
    */
    
    /*
    方法二:带备忘的自顶向下法:
        这种方法仍然按照自然的递归形式编写过程,但过程会保存每个每个子问题的解,在需要使用一个子问
    题的解的时候,先查找这个子问题的解是否已经存在。
        伪代码:
        MEMOIZED-CUT-ROD(p, n)
            let r[0 .. n] be a new array
            for i = 0 to n
                r[i] = -∞
            return MEMOIZED-CUT-ROD-AUX(p, n, r)
    
        MEMOIZED-CUT-ROD-AUX(p, n, r)
            if r[n] >= 0
                return r[n]
            if n == 0
                q = 0
            else q = -∞
                for i = 1 to n
                    q = max(q, p[i] + MEMOIZED-CUT-ROD-AUX(p, n - i, r))
                r[n] = q
            return r[n]
        MEMOIZED-CUT-ROD 函数主要进行一些初始化,因为我们已经知道结果肯定是非负的。
        在 MEMOIZED-CUT-ROD-AUX 中,n 代表要求的子问题,r 数组用来记忆。这种求解方法还是
    从最大问题开始逐渐将问题划分为子问题。所以称为自顶向下。
    */
    
    double memoizedCutRodAux(double *table, int length, double *r) {
        if (r[length] >= 0)
            return r[length];
        double q = 0;
    
        if (length > 0) {
            q = -1;
            for (int i = 1; i <= length; ++i)
                q = max(q, table[i] + memoizedCutRodAux(table, length - i, r));
        }
        r[length] = q;
        return q;
    }
    
    double memoizedCutRod(double *table, int length) {
        double *r = new double[length + 1];
        for (int i = 1; i <= length; ++i)
            r[i] = -1;
        return memoizedCutRodAux(table, length, r);
    }
    
    /*
    方法三:自底向上
        这种方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的”
    子问题的求解。因而我们可以将子问题按规模排序。按照由小到大的顺序进行求解。当求解某个子问题的
    时候,它所以来的那些更小的子问题都已经求解完毕,结果已经保存。这样每个子问题都只需要求解一次。
        这种方法和第二中的时间复杂度是一样的,但它通常有更小的系数。
        伪代码:
        MEMOIZED-CUT-ROD(p, n)
            let r[0 .. n] be a new array
            r[0] = 0
            for i = 1 to n
                q = -∞
                for j = 1 to i
                    q = max(q, p[j] + r[i - j])
                r[i] = q
            return r[n]
    
        自底向上版本 MEMOIZED-CUT-ROD 采用子问题的自然顺序,过程一次求解规模为 0, 1 .. n 的子问题。
    
        子问题图:
       自底向上动态规划是按“逆拓扑排序”或“反序的拓扑排序”来处理子问题图中的顶点。那么也就是说,对于任何一个 子问题,直至它所依赖的所有子问题均已求解完成,才会求解它。
    */ double memoizedCutRodDown(double *table, int length) { double *r = new double[length + 1]; r[0] = 0; double q = -1; for (int i = 1; i <= length; ++i) { q = -1; for (int j = 1; j <= i; ++j) { q = max(q, table[j] + r[i - j]); } r[i] = q; } return r[length]; } int main(int argc, char const *argv[]) { int steelLength; double *table; cout << "Input the length of the steel: "; cin >> steelLength; table = new double[steelLength + 1]; cout << "Input the price price table (only the price with ascending order [1 .. length]):" << endl; for (int i = 1; i <= steelLength; ++i) { cin >> table[i]; } cout << "Max price(first): " << cutRod(table, steelLength) << endl; cout << "Max price(second): " << memoizedCutRod(table, steelLength) << endl; cout << "Max price(thied): " << memoizedCutRodDown(table, steelLength) << endl; return 0; }
  • 相关阅读:
    break,continue,return的区别
    java中for循环的优化
    Jquery中click函数调用遇到的诡异问题
    MVC中Controller与View的类型绑定问题
    基于8019芯片的在9S12下移植成功的TCP/IP协议族(续)
    MVC中Partialiew使用的一点小总结
    基于8019芯片的在9S12下移植成功的TCP/IP协议族(一)
    9S12单片机的模块驱动程序备忘
    prim算法(zoj1203)
    win7下安装fedora
  • 原文地址:https://www.cnblogs.com/xiezhw3/p/4070348.html
Copyright © 2011-2022 走看看