动态规划解决分割问题
关于钢条切割问题的具体描述请看下面的博客:
http://www.cnblogs.com/tgycoder/p/4980655.html
写的很不错,基本把算法导论上的这一章的精华都写出来了,我也贴出我的代码,基本和书上差不多,只是把伪代码转化为可以运行的C++代码,代码转换的过程中也加深了对算法的理解。
代码如下:
#include<iostream> #include<cmath> #include<algorithm> using namespace std; int p[] = { 0,1,5,8,9,10,17,17,20,24,30 }; //钢条价格表 /*自顶向下递归实现钢条切割问题*/ int Cut_Rod(int p[], int n) { if (n == 0) { return 0; } int q = -65535; //此变量记录最大收益 for (int i = 1; i <= n; i++) { q = max(q, p[i] + Cut_Rod(p, n - i)); } return q; } /*自顶向下带记录递归实现钢条切割问题*/ int Memoized_Cut_Rod_Aux(int *p, int n,int r[]) { int q; if (r[n] >= 0) return r[n]; if (n == 0) q = 0; else { q = -65535; 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,int n) { int r[11]; //此处改为动态分配内存比较好 for (int i = 0; i <= n; i++) { r[i] = -65536; } return Memoized_Cut_Rod_Aux(p, n, r); } /*自底向上实现钢条切割问题*/ int Bottom_up_Cut_Rod(int p[], int n, int s[]) { int r[11]; //int s[11]; //用来记录具体的切割方案 r[0] = 0; for (int j = 1; j <= n; ++j) { int q = -65535; 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; } return r[n]; } void print_sn(int *s,int n) { while (n > 0) { cout << s[n]; n = n - s[n]; } cout << endl; } int main() { //cout << Cut_Rod(p, 7) << endl; //cout << Memoized_Cut_Rod(p, 7) << endl; int *s = new int[11]; //用来记录具体的分割方案 cout << Bottom_up_Cut_Rod(p, 9,s) << endl; print_sn(s, 9); return 0; }
第一次接触动态规划,我认为动态规划的关键在于可以解决子问题重叠的情况,即不同的子问题具有公共的子子问题,动态规划对其中的子子问题只求解一次,并把结果保存下来,避免做相同的工作,这可以大大提高算法的性能。
总结:结合动态规划的算法设计步骤来说,钢条切割问题也是遵守其标准的。
第一步先确定最优化解的结构特征:最优切割方案是由第一次切割后得到的两段钢条的最优切割方案组成的,或者是第一次切割后,其右端剩余部分的最优切割方案组成的。
第二步递归定义最优解的值,由上面的分析我们可以得到rn = max(pn,r1+rn-1,r2+rn-2,…,rn-1+r1)和rn = max(pi + rn-i) 两个最优解的公式,其满足求得原问题最优解的条件。
第三步根据得到的求最优解公式,计算出结果。我们用到了两种方法:带备忘的自顶向下递归法和自底向上法(非递归)。
第四步构造出最优解。
好久没有写博客了,期间发生了一些事扰乱了计划,现在要重新回归算法,为明年的华为算法比赛准备
good luck to me
夜深了,时间不多了