动态规划之钢条切割问题,《算法导论》15.1

  • 动态规划(dynamic programming),是一种解决问题的方法,它的思路是将大问题分解为子问题,然后合并子问题的解。与分治法将问题分解为彼此独立的子问题不同,动态规划应用于子问题相互重叠的情况。为了避免这些重叠部分(即子子问题)被重复计算,动态规划算法对每个子子问题只计算一次,并记录在一个表格中,这正是programming名字的由来,取其表格之意。

  • 动态规划通常用来求解“最优化问题”,这类问题可以有多种解决方案,每种解决方案对应一个值,我们希望找到具有最大值或最小值(最优值)的解决方案。注意,最优值只有一个,但是最优解决方案可能有多个,因为可能同时有多个解决方案都达到最优值。

    We typically apply dynamic programming to optimization problems. Such problems can have many possible solutions. Each solution has a value, and we wish to find a solution with the optimal (minimum or maximum) value. We call such a solution an optimal solution to the problem, as opposed to the optimal solution,since there may be several solutions that achieve the optimal value.

  • 解决这类问题的关键是找出所有的子问题,以及子问题之间的依赖关系。

    When we think about a dynamic-programming problem,we should understand the set of subproblems involved and how subproblems depend on one another.

  • 钢条切割问题,给定一段长度为n的钢条和一个价格表pi(i = 1,2,...,n),求切割钢条方案,使得销售收益r(n)最大。

#include<iostream>
using namespace std;

int p[10] = {1,5,8,9,10,17,17,20,24,30};    //p[i] means price when length = i+1

//参数p代表“长度-价格对照表”,n代表待切割钢条总长度。函数打印最优值和一个最优解决方案,并返回最优值。
//这是一个从r[1]~r[n]自底向上的求解过程,耗时O(n^2)
int bottom_up_cut_rod(int p[], int n)
{
    int* r = new int[n+1]; //r[i] means max revenue when length = i
    int* s = new int[n];//s[i] means first piece size when length = i+1
    r[0] = 0;
    int rx,sx,tmp;
    for(int i = 1; i != n+1; ++i)
    {
        rx = -1;
        for(int j = 0; j != i; ++j)
        {
            tmp = p[i-j-1] + r[j];
            if(rx < tmp)
            {
                rx = tmp;
                sx = i-j;
            }
        }
        r[i] = rx;
        s[i-1] = sx;
    }
    //print the optimal value and a optimal solution
    cout << "r" << n << " = " << r[n] << ", 切割方案 " << n << " = ";
    for(int l = n; l != 0; )
    {
        cout << s[l-1];
        l = l - s[l-1];
        if(l)
            cout << "+";
    }
    cout << endl;

    rx = r[n];
    delete[] r;
    delete[] s;
    return rx;
}

void test()
{
    for(int  n = 0; n != 11; ++n)
        bottom_up_cut_rod(p, n);
}
/*output:
r0 = 0, 切割方案 0 = 
r1 = 1, 切割方案 1 = 1
r2 = 5, 切割方案 2 = 2
r3 = 8, 切割方案 3 = 3
r4 = 10, 切割方案 4 = 2+2
r5 = 13, 切割方案 5 = 3+2
r6 = 17, 切割方案 6 = 6
r7 = 18, 切割方案 7 = 6+1
r8 = 22, 切割方案 8 = 6+2
r9 = 25, 切割方案 9 = 6+3
r10 = 30, 切割方案 10 = 10
*/
  • 课后习题
  • 15.1-2 用总长度为3的钢条就可以举出反例。

    假设我们要对长度为3的钢条进行切割,根据钢条密度的定义,当钢条长度为1、 2、 3时,密度依次为d1= p1, d2 = p2 / 2, d3 = p3/3;

    下面让我们枚举所有可能切割方案以及总收益r:

    方案1:3 = 3, r1 = d3 * 3;
    方案2:3 = 2+1, r2 = d2 * 2 + d1;
    方案3:3 = 1+1+1, r3 = d1 * 3;

    按照题目所描述的“贪心”策略,在已知d2是三个密度中最大的情况下,就一定能得出方案2是最优解。
    用公式表达,就是:已知d(2)>d(1),d(2) > d(3), 有r2>r1,r2>r3。

    成不成立呢?已知d2>d1, 可得r2-r3 = 2(d2-d1) > 0, 于是r2>r3得证。

    而r2-r1 = 2(d2-d3)+(d1-d3), 只要(d1-d3)足够小,就可以令r2-r1 < 0,就构成了反例。

    比如d1=1, d2=5, d3=4 就构成一个反例。

猜你喜欢

转载自www.cnblogs.com/meixiaogua/p/9897888.html