动态规划特点:
1. 把原始问题划分为一系列子问题。
2. 求解每个子问题仅一次,并将其结果保存在一个表中,以后用到时直接存取,不重复计算,节省时间。
3. 自底向上的计算。
适用范围:
一类优化问题:可分为多个相关子问题,子问题的解被重复适用。
适用动态规划的条件:
1. 优化子结构
a. 当一个问题的最优解包含了子问题的优化解时,我们说这个问题具有优化子结构。
b. 缩小子问题集合,只需那些优化问题中包含的子问题,降低实现复杂性。
c. 优化子结构使得我们能自下而上地完成求解过程
2. 重叠子问题
在问题的求解过程中,很多子问题的解将被多次使用。
动态规划的设计模式:
1. 分析优化解的结构
2. 递归地定义最优解的代价
3. 自底向上地计算优化解的代价并保存之,并获取构造最优解的信息。
4. 根据最优解的信息构造优化解
实例简介(参考算法导论教材第15章):
1. 装配线调度问题:
#include <iostream>
using namespace std;
int main(){
int m[2][6]={0};//记录装配的时间代价
int s[2][6]={0};//记录装配的路径选择
int a[2][6]={{7,9,3,4,8,4},{8,5,6,4,5,7}};
int e1=2,e2=4,x1=3,x2=2;
int t[2][5]={{2,3,1,3,4},{2,1,2,2,1}};
m[0][0]=e1+a[0][0];
m[1][0]=e2+a[1][0];
for(int j=1;j<6;j++){
for(int i=0;i<2;i++){
int temp1=m[1-i][j-1]+t[1-i][j-1]+a[i][j];
int temp2=m[i][j-1]+a[i][j];
if(temp1<temp2){
m[i][j]=temp1;
s[i][j]=1-i+1;
}
else{
m[i][j]=temp2;
s[i][j]=i+1;
}
}
}
for(int i=0;i<2;i++){
for(int j=0;j<6;j++){
cout<<m[i][j]<<" ";
}
cout<<endl;
}
for(int i=0;i<2;i++){
for(int j=0;j<6;j++){
cout<<s[i][j]<<" ";
}
cout<<endl;
}
int line;
for(int i=5;i>=0;i--){
if(i==5){
if(m[0][i]+x1<m[1][i]+x2){
cout<<"The shortest time consuming is "<<m[0][i]+x1<<endl;
cout<<"line:"<<endl;
line=0;
cout<<line+1<<" ";
}
else{
cout<<"The shortest time consuming is "<<m[1][i]+x2<<endl;
cout<<"line"<<endl;
line=1;
cout<<line+1<<" ";
}
}
cout<<s[line][i]<<" ";
line=s[line][i]-1;
}
cout<<endl;
}
2. 矩阵乘法问题:
#include <iostream>
#include <string>
using namespace std;
void PrintOptimal(int s[][6],int i,int j){//打印优化解结果
if(i==j)
cout<<i+1;
else{
cout<<'(';
PrintOptimal(s,i,s[i][j]-1);
PrintOptimal(s,s[i][j],j);
cout<<')';
}
}
int main(){
int p[7]={30,35,15,5,10,20,25};
int m[6][6]={0};//记录乘积计算代价
int s[6][6]={0};//记录k值选择标记
const int n=6;
for(int i=1;i<n;i++){
for(int j=0;j<n-i;j++){
int temp=1000000;
for(int k=j;k<j+i;k++){
int tmp=m[j][k]+m[k+1][j+i]+p[j]*p[k+1]*p[j+i+1];
if(temp>tmp){
s[j][j+i]=k+1;
temp=tmp;
}
}
m[j][j+i]=temp;
}
}
for(int i=0;i<6;i++){
for(int j=0;j<6;j++){
cout.width(5);
cout<<m[i][j]<<" ";
}
cout<<endl;
}
for(int i=0;i<6;i++){
for(int j=0;j<6;j++){
cout.width(5);
cout<<s[i][j]<<" ";
}
cout<<endl;
}
PrintOptimal(s,0,5);
cout<<endl;
}
3. 最长公共子序列问题:
#include <iostream>
#include <string>
using namespace std;
int main(){
string str1,str2,str3;
str1="abcbdab";
str2="bdcaba";
str3="";
int m[8][7]={0};
pair<int,int> s[8][7]={make_pair(0,0)};
for(int i=1;i<8;i++){
for(int j=1;j<7;j++){
if(str1[i-1]==str2[j-1]){
m[i][j]=m[i-1][j-1]+1;
s[i][j]=make_pair(-1,-1);
}
else{
if(m[i-1][j]<m[i][j-1]){
m[i][j]=m[i][j-1];
s[i][j]=make_pair(0,-1);
}
else
{
m[i][j]=m[i-1][j];
s[i][j]=make_pair(-1,0);
}
}
}
}
for(int i=0;i<8;i++){
for(int j=0;j<7;j++){
cout<<m[i][j]<<" ";
}
cout<<endl;
}
for(int i=0;i<8;i++){
for(int j=0;j<7;j++){
cout.width(2);
cout<<s[i][j].first;
cout<<",";
cout.width(2);
cout<<s[i][j].second<<" ";
}
cout<<endl;
}
cout<<"The longest common string length is "<<m[7][6]<<endl;
for(int i=7,j=6;i>0&&j>0;){
if(s[i][j].first==-1&&s[i][j].second==-1){
str3=str1[i-1]+str3;
}
int temp=s[i][j].second;
i+=s[i][j].first;
j+=temp;
}
cout<<"The longest common sring is "<<str3<<endl;
}