摘要
动态规划(Dynamic programming)通过组合子问题来来求解原问题(在此处,我们不妨将"programming"理解为一种表格,用于记录上一步执行的结果)。动态规划算法对每个子问题只求解一次(而递归算法重复求解相同的子问题,因而效率非常低),并将其解保存在一个表格中,从而不必每次重复求解相同子问题,动态规划算法效率大大高于递归算法就得益于此。动态规划算法在数学、管理科学、计算机科学、经济学和生物信息学中广泛应用。本文以钢条切割问题和最长公共子序列问题为例讲解动态规划算法。
一. 钢条切割问题
将一段钢条切割成若干段,每段钢条长度为整数,每段钢条价格如下表所示,若要获得最大收益应如何切?
此处参考《算法导论》中的叙述(见下图)
一般地,对于rn(n≥1),用下式表示收益:
rn=max(pn,r1+rn-1,r2+rn-2,…,rn-1+r1)
上式可进一步简化为 rn=max(pi+rn-i), 其中1≤i≤n.此式称为状态转移方程,由此可有三种方案解决,分别为:自顶向下递归实现、带备忘的自顶向下法(top-down with memoization)和自底向上向上法(bottom-up method),其中后两种方案属于动态规划动态规划方法。下面分别用代码实现之:
1.递归方法:
#include <iostream>
#include<algorithm>
using namespace std;
#define N 100
int CUT_ROD(int p[N],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;
}
int main()
{
int v[N]={
0,1,5,8,9,10,17,17,20,24,30};
cout<<"钢条切割的最大收益为:"<<endl;
cout<<CUT_ROD(v,10)<<endl;
return 0;
}
2.带备忘的自顶向下法
#include <iostream>
#include <algorithm>
using namespace std;
#define N 100
int MEMOIZED_CUT_ROD_AUX(int p[N],int n,int r[N]) //带备忘机制的递归函数
{
int q;
if(r[n]>=0)
return r[n];
if(n==0