基本概念
动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题,动态规划算法对于每个子子问题只求解一次,并将其保存,避免了重复的计算。
动态规划算法设计的四个步骤:
1.刻画一个最优解的结构特征。
2.递归地定义最优解的值。
3.计算最优解的值通常采用自底向上的方法。
4.利用计算出的信息构造一个最优解。
典型问题
1.钢条切割问题
给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,…,n),求切割钢条的最佳方案使得销售收益rn最大。
解:
状态:定义:d(i)为长度为i英寸钢条的最大收益
状态转移方程: d(n)=max{pn,d(1)+d(n-1),d(2)+d(n-2),…d(n-1)+d(1)},这时也就刻画出了最优解的结构特征,同时也可以看出钢条切割问题满足最优子结构性质。
该问题的状态转移方程也可以转化为d(n) = pi+d(n-i) i=0…n
在动态规划算法中需要仔细安排求解顺序,对于每个子问题之求解一次,并将结果保存下来,所以需要使用额外的内存空间将中间结果保存下来,这也是个典型的时空权衡的例子。
int *cut_rod(int n,int *p){
int *r = new int[n];
r[0] = 0;
for(int i = 1 ; j <= n ; j++){
q = MIN;
for(int i = 1 ; i <= j ; j++)
q=max(q,p[i]+r[j-i]);
r[j]=q;
}
return r[n];
}
2.0-1背包问题
共有n个宝石编号: 0,1,2,…,n-1。 某人身上有一个背包,背包的容量为C。第i个宝石对应的体积和价值分别为V[i]和W[i] 。要装下哪些宝石才能获得最大的利益呢?
状态: d(i,j)表示前i个宝石装到剩余体积为j的背包里能达到的最大价值
状态转移方程: d(i) = max{1, d(j)+1},其中j
#include <iostream>
using namespace std;
int lis(int A[], int n){
int *d = new int[n];
int len = 1;
for(int i=0; i<n; ++i){
d[i] = 1;
for(int j=0; j<i; ++j)
if(A[j]<=A[i] && d[j]+1>d[i])
d[i] = d[j] + 1;
if(d[i]>len) len = d[i];
}
delete[] d;
return len;
}
int main(){
int A[] = {
5, 3, 4, 8, 6, 7
};
cout<<lis(A, 6)<<endl;
return 0;
}