动态规划通常用于解决最优化问题,在这类问题中,通过做出一组选择来达到最优解。在做出每个选择的同时,通常会生成与原问题形式相同的子问题。当多于一个选择子集都生成相同的子问题时,动态规划技术通常就会很有效,其关键技术就是对每个这样的子问题都保存其解,当其重复出现时即可避免重复求解。
一 钢条切割问题
Serling公司购买长钢条,将其切割为短钢条出售。切割工序本身没有成本支出。公司管理层希望知道最佳的切割方案。假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元)。钢条的长度均为整英寸。图15-1给出了一个价格表的样例。
钢条切割问题是这样的:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,…n),求切割钢条方案,使得销售收益rn最大。注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割。
二 解决方案
1 暴力递归(多叉树遍历)
时间复杂度:2^n
伪代码如下:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
| int cutrod(vector<int> & p,int n ){ if(n == 0) return 0; int q = INT_MIN; for(int i = 1; i <= n; ++i){
q = max(q,p[i]+cutrod(p, n-i)); } return q; }
|
递归过程:
2 带备忘录的递归(自顶向下)
伪代码:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int mem_cutrodHelper(vector<int> & p,int n,vector<int> & r){ if(r[n] > 0) return r[n]; int q = INT_MIN; if(n == 0) q = 0; else{ for(int i = 1 ; i <= n; ++i){ q = max(q,p[i]+mem_cutrodHelper(p, n-i, r)); } } r[n] = q; return q; } int mem_cutrod(vector<int>& p,int n){ vector<int> r(n + 1,-1); return mem_cutrodHelper(p,n,r); }
|
3 动态规划(自底向上)
伪代码:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int dp_cutrod(vector<int> & p,int n ){ vector<int> dp(n+1); dp[0] = 0; for(int i = 1; i <= n; ++i){ int q = -1; for(int j = 1 ; j <= i;++j){ q = max(q,p[j] + dp[i -j]); } dp[i] = q; } return dp[n]; }
int dp_cutrod2(vector<int> & p,int n ){ vector<int> dp(n+1); dp[0] = 0; for(int i = 1; i <= n; ++i){ for(int j = 1 ; j <= i;++j){ dp[i] = max(dp[i],p[j] + dp[i -j]); } } return dp[n]; }
|
4 代码重构
伪代码:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| int print_dp_cutrod(vector<int> & p,int n){ vector<int> cutpos(n+1); vector<int> dp(n + 1); dp[0] = 0; for(int i = 1; i <= n; ++i){ int q = -1; for(int j = 1; j <= i; j++){ int temp = p[j] + dp[i - j]; if(q < temp){ q = temp; cutpos[i] = j; } } dp[i] = q; } std::cout<<"钢条的切割位置:"; for(int i = 0 ; i < n+1;i++){ std::cout<< cutpos[i] << " "; } std::cout<<endl; std::cout<<"dp数组:"; for(int i = 0 ; i < dp.size();i++){ cout<<dp[i]<<" "; } std::cout<<endl; cout << "最优切割策略: "; for(int i = cutpos.size()-1; i > 0; i -= cutpos[i]){ cout<<cutpos[i]<<" "; } cout<<endl; return dp[n]; }
|
正确输出: