动态规划

( dynamic programming)与分治方法相似,都是通过组合子问题的解来求解原问题(

这里,programming”指的是一种表格法,并非编写计算机程序)。分治方法将问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来,求出原问题的解。与之相反,动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治算法会做许多不必要的工作,它会反复地求解那些公共子子问题。而动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了这种不必要的计算工作。动态规划方法通常用来求解最优化问题( optimization problem)。这类问题可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值(最小值或最大值)的解。我们称这样的解为问题的一个最优解( an optimal solution),而不是最优解( the optimal solution),因为可能有多个解都达到。

我们第一个应用动态规划的例子是:

求解一个如何切割钢条的简单问题。 Serling公司条,将其切割为短钢条出售。切割工序本身没有成本支出。公司管理层希望知道最佳的切假定我们知道 Serling公司出售一段长度为i英寸的钢条的价格为p,(i=1,2,…,单元)。钢条的长度均为整英寸。

长度i

1

2

3

4

5

6

7

8

9

价格pi

1

5

8

9

10

17

17

20

30

切割钢条方案,使得销售收益r最大。注意,如果长度为n英寸的钢条的价格pn,足够大这不需要切割:给定一段长度为n英寸的钢条和一个价格表p(i=1,2,

考虑m=4的情况。图15-2给出了4英寸钢条所有可能的切割方案,包括根本不切割的方案。

切割钢条方案,使得销售收益r最大。注意,如果长度为n英寸的钢条的价格pn,足够大这不需要切割:给定一段长度为n英寸的钢条和一个价格表p(i=1,2,

考虑m=4的情况。图15-2给出了4英寸钢条所有可能的切割方案,包括根本不切割的方案。

 

长度为n英寸的钢条共有2n-1中方案。

我们用普通的加法符号表示切割方案,因此7=2+2+3表示将长度为7英寸的钢条切割为三段一两段长度为2英寸、一段长度为3英寸如果一个最优解将钢条切割为k(对某个1kn),那么最优切割方案

n=i1+i2+…in

将钢条切割为长度分别为i,,…,a的小段,得到最大收益

r(n)=p1+p2++pn

对于上述价格表样例,我们可以观察所有最优收益值r(=1,2,…,10)及对应的最优切割方案

r1=1,切割方案1=1(无切割)

r2=5,切割方案2=2(无切割

r3=8,切割方案3=3(无切割)

r4=10,切割方案4=2+

r5=13,切割方案5=2

r6=17切割方案6=6(无切

r7=18,切割方案7=1+67=2+2+3

r8=22,切割方案8=2+6

r9=25,切割方案9=3+6

n=30,切割方案10=10(无切割)

们将钢条从左边切割下长度为i的一段,只对右边剩下的长度为n-i的一段继续进行切割(递归求解)对左边的一段则不再进行切割。即问题分解的方式为:将长度为n的钢条分解为左边开始一段以及剩余部分继续分解的结果。

递归求解

下面给出递归求解的方法:

#include <iostream>
#include <string>
#include<stdlib.h>
using namespace std;
int pi[] = {0,1,5,8,9,10,17,17,20,24,30 };
int cutRod(int pi[], int n);
int main()
{
  int maxprice= cutRod(pi, 9);
  cout << maxprice;

}
int cutRod(int pi[],int n)
{
  if (n == 0)
    return 0;
  int q = -1; 
  for (int i = 1; i <= n; i++)
  {
    int m = pi[i] + cutRod(pi, n - i);
      q = __max(q, m);
  }
  return q;
}
#include <iostream>
#include <string>
#include<stdlib.h>
int memoCouRod(int pi[], int n, int r[]);
using namespace std;
int pi[] = {0,1,5,8,9,10,17,17,20,24,30 };
int cutRod(int pi[], int n);
int main()
{
  int maxprice= cutRod(pi, 9);
  cout << maxprice;

}
int cutRod(int pi[],int n)
{
  int* r = new int[n];
  for (int i = 1; i <= n; i++)
    r[i] = -1;
  return memoCouRod(pi, n, r);
}
int memoCouRod(int pi[], int n, int r[])
{
  if (r[n] > 0)
    return r[n];
  else
  {
    int q = -1;
    if (n == 0)
      q = 0;
    else
    {
      for (int i = 1; i <= n; i++)
      {
        int m = pi[i] + memoCouRod(pi, n - i, r);
          q = __max(q, m);
      }
      return r[n] = q;
    }
   
  }

}

 

第一种方法称为带备忘的自顶向下法( p-down with memoization)。此方法仍按自然的递白形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中)。当需要一个今于向题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了机算时间;否则,按通常方式计算这个子问题。我们称这个递归过程是带备忘的( memoized),因为它“记住”了之前已经计算出的结果

第二种方法称为自底向上法( bottom-up method),这种方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的”子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次,当我们求解它(也是第一次遇到它)时,它的所有前提子问题都已求解完成
两种方法得到的算法具有相同的渐近运行时间,仅有的差异是在某些特殊情况下,自顶向下方法并未真正递归地考察所有可能的子问题。由于没有频繁的递归函数调用的开销,自底向上方法的时间复杂性函数通常具有更小的系数。

带备忘的自顶向下法

#include <iostream>
#include <string>
#include<stdlib.h>
int memoCouRod(int pi[], int n, int r[]);
using namespace std;
int pi[] = {0,1,5,8,9,10,17,17,20,24,30 };
int cutRod(int pi[], int n);
int main()
{
  int maxprice= cutRod(pi, 9);
  cout << maxprice;

}
int cutRod(int pi[],int n)
{
  int* r = new int[n];
  for (int i = 1; i <= n; i++)
    r[i] = -1;
  return memoCouRod(pi, n, r);
}
int memoCouRod(int pi[], int n, int r[])
{
  if (r[n] > 0)
    return r[n];
  else
  {
    int q = -1;
    if (n == 0)
      q = 0;
    else
    {
      for (int i = 1; i <= n; i++)
      {
        int m = pi[i] + memoCouRod(pi, n - i, r);
          q = __max(q, m);
      }
      return r[n] = q;
    }
   
  }

}

 

自底向上法

#include <iostream>
#include <string>
#include<stdlib.h>
using namespace std;
int bottonCouRod(int pi[], int n, int r[]);
int pi[] = {0,1,5,8,9,10,17,17,20,24,30 };
int cutRod(int pi[], int n);
int main()
{
  int maxprice= cutRod(pi, 4);
  cout << maxprice;

}
int cutRod(int pi[],int n)
{
  int* r = new int[n];
  for (int i = 1; i <= n; i++)
    r[i] = -1;
  return  bottonCouRod(pi, n, r);
}
int bottonCouRod(int pi[], int n, int r[])
{
  r[0] = 0;
  for (int j = 1; j <= n; j++)
  {
    int q = -1;
    for (int i = 0; i <= j; i++)
    {
      q = __max(q, pi[i] + r[j - i]);
    }
    r[j] = q;
  }
  return r[n];
}

 

版权声明:本文为king0原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/king0/p/10504873.html