动态规划之钢条问题

来源:互联网 发布:在淘宝搜什么有福利 编辑:程序博客网 时间:2024/06/06 09:18

先给出概述:动态规划通常是用来解决最优化问题的。最优化问题指的是该类问题有很多可行解,我们希望找到最优解(最大或最小的解),更具体的来说:要得到规模为n的问题的最优解,那么会用到规模为n-1的相似问题的最优解。

下面我们将通过钢条切割问题来详细描述什么是动态规划,动态规划又是如何操作的。

钢条问题描述:给定一段长度为n的钢条和它对应的收益表Pi(i=1--n),问如何切割钢条(切割钢条本身不计成本)使得收益最大。


我们可以很容易想出用递归的方式来求解:

1.分解:假设我们知道最优的方案将在i处进行切割(当然我们不知道,所以用i表示,然后进行一次for循环即可

那么问题变为求解长度为i及长度为(n-i)的钢条的最大收益。

2.解决:递归地求解,直到长度为1那么方案只有一个。

3.合并:将长度为i及长度为(n-i)的钢条的最大收益相加即为长度为n的钢条的最大收益。

int CutRod(int* p, int n) {if (n == 0)return 0;int res = INT_MIN;for (int i = 1; i <= n; i++) {res = std::max(res, p[i] + CutRod(p, n - i));}return res;}//自顶向下的递归,很容易理解的递归。算法时间为指数增长2的n次方。


上面是简单递归版本的代码。

分析:我们将该过程与归并排序时的递归进行对比分析点击打开链接,发现最大的不同在于:归并排序将数组分成两个数组后,对两个数组进行排序的过程是不相关的,而该问题将问题分解为求解长度为i及长度为(n-i)的钢条的最大收益时两个问题是会重叠的(比如说i=n-i,当然求两次长度为i的最大收益就很蠢了)——子问题发生重叠。另外,要想求解长度为n的最优解,那么必须知道长度为n-1的最优解———这种性质叫最优子结构

我们发现如果我把子问题的最优解保存下来,那么在反复求解该子问题最优解时直接用结果将大大降低算法的时间,这就是动态规划的思想实质——我们并不是只用程序开始时的信息,也保存并使用了程序运行过程中产生对接下来问题求解有利的信息。

以下是自底向上的动态规划代码:

int BottomUpCutRodOfDynamicProgram(int* p, int n) {int res[11];res[0] = 0;for (int j = 1; j <= n; j++) {int temp = INT_MIN;for (int i = 1; i <= j; i++) {temp = std::max(temp, p[i] + res[j - i]);}res[j] = temp;}return res[n];}//两个for,于是他的时间复杂度大大降低为n的平方。


代码阐述:建立额外的数组res来保存长度为i(数组下标)的最优解。

那通常在实际操作中我们不仅要求算出最大收益,还需要给出最大收益时的切割方案,于是有以下代码(包括main函数和输出)

int ExtendBottomUpCutRodOfDynamicProgram(int* p, int n,int* plan) {int res[11];res[0] = 0;for (int j = 1; j <= n; j++) {int temp = INT_MIN;for (int i = 1; i <= j; i++) {if (temp < p[i] + res[j - i]) {temp =  p[i] + res[j - i];plan[j] = i;}}res[j] = temp;}return res[n];}//用数组plan来存储方案。void PrintPlan(int* plan, int n) {while (n) {std::cout << plan[n] << std::endl;n -= plan[n];}}int main() {int plan[11];plan[0] = 0;int price[11]{ 0,1,5,8,9,10,17,17,20,24,30 };std::cout << CutRod(price, 5)<<std::endl;std::cout << BottomUpCutRodOfDynamicProgram(price, 5) << std::endl;std::cout << ExtendBottomUpCutRodOfDynamicProgram(price, 5, plan)<< std::endl;PrintPlan(plan, 5);}


最后就来总结下动态规划:

采用动态规划求解的问题应该具备最优子结构(一个问题的最优解包含其子问题的最优解)和子问题重叠(有相同的子问题要解决)

其通用模式是:

1.求解问题的最优解的第一步是做出一个选择,(列如,钢条问题中选择第一次的切割位置)在做出选择后会产生一个或多个待解决的子问题。

2.确定所产生的子问题,清楚如何最好滴刻画子问题空间(通俗的说就是什么样的子问题会重叠一定要搞清楚,因为我们要保存的就是这些重叠的子问题。它是一个线索。)


原创粉丝点击