动态规划方法通常用来求解最优化问题。动态规划算法设计步骤:
1.刻画一个最优解的结构特征。
2.递归定义最优解的值。
3.计算最优解的值,通常采用自底向上的方法。
4.利用计算出的信息构造一个最优解。
动态规划的实现方法:
带备忘的自顶向下法:此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中)。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。
自底向上法:这种方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都依赖于“更小的”子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已经求解完毕,结果已经保存。每个子问题只需要求解一次,当我们求解它(也是第一次遇到它)时,它的所有前提子问题都已求解完成。
问题:公司购买长钢条,将其切割为短钢条出售。切割工序本身没有成本支出。公司管理层希望知道最佳的切割方案。
假定我们知道公司出售一段长度i英寸的钢条的价格为pi(i=1,2,...,单位为美元)。钢条的长度均为整英寸。图给出了一个价格表的样例。
长度 i 1 2 3 4 5 6 7 8 9 10
价格 Pi 1 5 8 9 10 17 17 20 24 30
切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大。
当然,如果长度为n英寸的钢条价格Pn足够大,最优解可能就是完全不需要切割。
对于上述价格表样例,我们可以观察所有最优收益值Ri及对应的最优解方案:
R1 = 1,切割方案1 = 1(无切割)
R2 = 5,切割方案2 = 2(无切割)
R3 = 8, 切割方案3 = 3(无切割)
R4 = 10, 切割方案4 = 2 + 2
R5 = 13, 切割方案5 = 2 + 3
R6 = 17, 切割方案6 = 6(无切割)
R7 = 18, 切割方案7 = 1 + 6或7 = 2 + 2 + 3
R8 = 22, 切割方案8 = 2 + 6
R9 = 25, 切割方案9 = 3 + 6
R10 = 30,切割方案10 = 10(无切割)
更一般地,对于Rn(n >= 1),我们可以用更短的钢条的最优切割收益来描述它:
Rn = max(Pn, R1 + Rn-1, R2 + Rn-2,...,Rn-1 + R1)
首先将钢条切割为长度为i和n - i两段,接着求解这两段的最优切割收益Ri和Rn - i(每种方案的最优收益为两段的最优收益之和),由于无法预知哪种方案会获得最优收益,我们必须考察所有可能的i,选取其中收益最大者。如果直接出售原钢条会获得最大收益,我们当然可以选择不做任何切割。
#include<iostream> #include<algorithm> #include<vector> #include<utility> using namespace std; /*------------------------------------------------------------------------------------------ /* 用朴素的递归的方法的求解:递归的求解每一个子问题,当碰到相同的子问题重新求解,重点理解递归的过程 /* 此方法最大的缺点是:每当n增加1的时候,程序运行时间差不多就会增加一倍, /* 其工作量会爆炸性的增长。 /*-------------------------------------------------------------------------------------------*/ int cut_rod(int *p,const int &n) { if (n == 0) return 0; int q = -1; for (int i = 1; i <= n; ++i) { q = max(q, p[i]+cut_rod(p, n - i)); } return q; } //使用动态规划的方法:在求其子问题的时候保存其子问题的结果,当遇到相同的子问题的时候不用再次求解 //而是查找求过的子问题直接调用 //第二种方法是:自顶向下cut_tod过程,加入了备忘机制 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 = max(q, p[i] + memoized_cut_rod_aux(p, n - i, r)); } r[n] = q;//保存子问题的一个最优解 return q; } int memoized_cut_rod(int *p, const int n) { int r[11] = {0}; for (int i = 1; i <= n; i++) r[i] = -1; int q = memoized_cut_rod_aux(p, n, r); return q; } //第三种方法:自底向上的方法 int bottom_up_ut_rod(int *p, int n) { int r[11] = { 0 }; int q = 0; for (int j = 1; j <= n; ++j) { for (int i = 1; i <= j; ++i) q = max(q, p[i] + r[j - i]); r[j] = q; } return r[n]; } //重构:不仅输出长度为n的时候最大收益还输出一个最优切割方案 pair<vector<int>, vector<int>> extended_bottom_up_cut_rod(int *p, int n) { int r[11] = { 0 }; int s[11] = { 0 }; pair< vector<int>, vector<int> > result; result.first.push_back(0); result.second.push_back(0); for (int j = 1; j <= n; ++j) { int q = -1; for (int i = 1; i <= j; ++i) { if (q < p[i] + r[j - i]) { q = p[i] + r[j - i]; s[j] = i; } } r[j] = q; result.first.push_back(r[j]); result.second.push_back(s[j]); } return result; } //输出收益和方案 void print_cut_rod_solution(int *p,int n) { pair< vector<int>, vector<int> > result; result = extended_bottom_up_cut_rod(p, n); vector<int> r = result.first; vector<int> s = result.second; cout << "钢条切割最大收益为: "; cout << r[n] << endl; cout << "最佳切割方式: " << endl; while (n > 0) { cout << s[n]; n = n - s[n]; cout << endl; } } int main() { char ch; int p[11] = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 }; int n; while (cin >> n) { cout << "普通递归方法: " << cut_rod(p, n) << endl; cout << "带备忘录的自顶向下法: " << memoized_cut_rod(p, n) << endl; cout << "自底向下法: " << bottom_up_ut_rod(p, n) << endl; print_cut_rod_solution(p, n); cout << "请输入 y or n:"; cin >> ch; if (ch == 'n') break; else { cout << "请输入继续输入适当的数n:"; continue; } } }
原文地址:http://blog.csdn.net/chenxun_2010/article/details/41904847