已知钢条切割的不同长度对应的不同价格如下所示:
长度i 1 2 3 4 5 6 7 8 9 10
价格pi 1 5 8 9 10 17 17 20 24 30
求输入长度,输出最佳的收益。
详细理论知识见《算法导论第十五章》 P359
书中给出三个算法:
一、自顶向下递归实现
缺点:当n足够大时,时间会爆炸性地增长。
伪代码:
CUT-ROD(p,n) if n==0 return 0 q=-MAX for i = 1 to n q=max(q,p[i]+CUT_ROD(p,n-i)) return q
其中q初始化为负无穷大(或者任意负数也行)p为一维数组代表各种长度的对应最佳方案
C++实现代码:
#include <iostream> //自顶向下递归实现 using namespace std; int p[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 }; int CUT_ROD(int p[],int n){ if (n == 0) return 0; int q = -1; //起始赋值为负数 for (int i = 1; i <= n; i++){ //for i = 1 to n if (q < p[i-1] + CUT_ROD(p, n - i)) //输入为p[1…n] q = p[i-1] + CUT_ROD(p, n - i); } return q; } int main() { int n; while (1) { cin >> n; int res = CUT_ROD(p, n); cout << res << endl; } }
二、带备忘的自顶向下法
引入备忘机制,递归解决问题,保存每个子问题的解。
伪代码:
MEMOIZED-CUT-ROD(p,n) let r[0...n]be a new array for i = 0 to n r[i] = -MAX return MEMOIZED-CUT-ROD-AUX(p,n,r) MEMOIZED-CUT-ROD-AUX(p,n,r) if r[n]>=0 return r[n] if n==0 q=0 else q=-MAX for i = 1 to n q = max(q,p[i]+MEMOIZED-CUT-ROD-AUX(p,n-i,r)) r[n]=q return q
和自底向下的递归实现类似,仅仅是多提供了一个数组作为存储。效率提高。
C++实现
#include <iostream> //自顶向下递归实现 using namespace std; int p[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 }; int r[10]; int n; 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 = -1; for (int i = 1; i <= n; i++) { if (q < p[i-1]+MEMOIZED_CUT_ROD_AUX(p, n - i, r)) q = p[i - 1] + MEMOIZED_CUT_ROD_AUX(p, n - i, r); } r[n] = q; return q; } int MEMOIZED_CUT_ROD(int p[], int n){ memset(r, -1, sizeof(r)); return MEMOIZED_CUT_ROD_AUX(p, n, r); } int main() { int n; while (1) { cin >> n; int res = MEMOIZED_CUT_ROD(p, n); cout << res << endl; } }
三、自底向上法
自底向上依赖于“更小的”子问题求解。用两个循环代替递归实现,每次所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次。
伪代码:
BOTTOM-UP-CUT-ROD(p,n) let r[0..n]be a new array r[0]=0 for j = 1 to n q = -MAX for i = 1 to j q = max(q,p[i]+r[j-i]) r[j]=q; return r[n]
C++实现代码:
#include <iostream> //自顶向下递归实现 using namespace std; int p[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 }; int r[10]; int n; int BOTTOM_UP_CUT_ROD(int p[],int n){ r[0] = 0; for (int j = 1; j <= n; j++){ int q = -1; for (int i = 1; i <= j; i++) { if (q < p[i - 1] + r[j - i]) q = p[i - 1] + r[j - i]; } r[j] = q; } return r[n]; } int main() { int n; while (1) { cin >> n; int res = BOTTOM_UP_CUT_ROD(p,n); cout << res << endl; } }
附上自底向下重构解(返回最优值以及最优值第一段长度)伪代码
MEMOIZED-CUT-ROD-AUX(p,n,r) let r[0..n] and s[0..n] be new arrays r[0]=0 for j=1 to n q = -1 for i=1 to j if q < p[i]+r[j-i] q = p[i]+r[j-i] s[j]= i ; r[j] = -MAX return MEMOIZED-CUT-ROD-AUX(p,n,r)
总结:
一直不理解动态规划,但是却在很多OJ上看到他的影子,今天在算法导论上算是收获颇丰,不过DP依旧是一道跨不过去但非得跨过去的障碍。
加油!
动态规划 - 钢条切割问题
最新推荐文章于 2022-03-29 22:47:10 发布