第四章 动态规划

基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

1.矩阵连乘问题

已知:给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,
计算:这n个矩阵的连乘积A1A2…An所需要的乘法运算的次数最小值。

分析:
将矩阵连乘积AiAi+1…Aj
简记为A[i:j], 这里i≤j;考察计算A[1:n]的最优计算次序。
设这个计算次序在矩阵Ak和Ak+1之间将矩阵链断开,1≤k<n
则其相应完全加括号方式为(A1A2…Ak)(Ak+1Ak+2…An)
计算量(乘法计算的次数)=:
A[1:k]的计算量+
A[k+1:n]的计算量+
A[1:k]和A[k+1:n]相乘的计算量
在这里插入图片描述



#define
NUM 51
int p[NUM];//记录矩阵的行列数的值,p[0]记录第一个矩阵行数,之后的数组分量依次记录其他矩阵的列数。数组分量的个数等于矩阵的个数+1
int m[NUM][NUM];//状态,记录子问题的最优值
int s[NUM][NUM];//记录子问题的最优解
void MatrixChain (int n){
  for (int i=1;  i<=n;  i++) m[i][i]= 0;//长度是1的矩阵链连乘的计算次数
  for (int r=2; r<=n;  r++)//矩阵链的长度,按照矩阵链长度递增的方法完成计算
       for(int i=1; i<=n-r+1;  i++) {      //连乘的起点
         int j=i+r-1;        //矩阵链中的最后一个矩阵的编号 
         m[i][j] = m[i+1][j]+p[i-1]*p[i]*p[j]; //m[i][j]的假设值
         s[i][j] = i; //断开位置的初值,记录即最优解的初值
  //计算m[i][j]的最小值,即确定断开的最佳位置,子问题的最优解      
for (int k=i+1;k<j;k++) {              
int t = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
       if(t < m[i][j]) {
                 m[i][j] = t; 
                 s[i][j] = k;}
  }  
}
}

避免相同子问题的求解,备忘录方法


int LookupChai  (int i, int j){
  if (m[i][j]>0) return m[i][j];
  if (i==j) return 0;
  int u = LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];
  s[i][j] = i;
  for (int k = i+1; k<j; k++) {
       intt = LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];
       if(t < u) { u = t; s[i][j] = k;}
  }
  m[i][j] = u;
  return u;
}

2.最长公共子序列

若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。

例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。

给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。

给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

分析:
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为Z={z1,z2,…,zk}
若xm=yn, 则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列。
若xm≠yn且zk≠xm, 则Z是Xm-1和Y的最长公共子序列。
若xm≠yn且zk≠yn, 则Z是X和Yn-1的最长公共子序列。



#define NUM 100
int c[NUM][NUM];  //x[1-i] 和 y[1-j]之间的公共子序列的长度。
int b[NUM][NUM]; // 辅助完成最优解得计算
void LCSLength (int m, int n, const char x[],char y[]){
  int i,j;
  //数组c的第0行、第0列置0
  for (i = 1; i <= m; i++) c[i][0] =0;
  for (i = 1; i <= n; i++) c[0][i] =0;
  //根据递推公式构造数组c 
  for (i = 1; i <= m; i++)     
for (j = 1; j <= n; j++){
           if (x[i]==y[j]) 
         {c[i][j]=c[i-1][j-1]+1; b[i][j]=1; }    //
           else if (c[i-1][j]>=c[i][j-1]) 
              {c[i][j]=c[i-1][j];
b[i][j]=2; }   //
           else { c[i][j]=c[i][j-1]; b[i][j]=3; }  //
  }
}
void LCS(int i,int j,char x[]){
       if
(i ==0 || j==0) return;
       if
(b[i][j]== 1){ 
                LCS(i-1,j-1,x); 
              cout<<x[i];
       }
       else
if (b[i][j]== 2) 
              LCS(i-1,j,x);
       else
              LCS(i,j-1,x);

}

3.最大字段和

给定由n个整数(包含负整数)组成的序列a1,a2,…,an,求该序列子段和的最大值。

计算最大字段和动态规划算法
#define NUM 1001
int a[NUM];
int MaxSum(int n){   
int sum=0; //最大子段和   
int b=0; //1~j的最大子段和,因无需保留其他结果,所以没必要定义数组   
for (int i=1;i<=n;i++)   
{
     if (b>0) 
           b+=a[i]; 
       else
           b=a[i];
       if(b>sum) sum=b;   
}   
return sum;
}




计算最大字段和动态规划算法的最优解

#define NUM 1001
int a[NUM];
int MaxSum(int n, int &besti, int &bestj){
  int sum=0; 
  int b=0;
  int begin = 0;
  for (int i=1; i<=n; i++){
       if (b>0)  b+=a[i]; 
       else
{b=a[i]; begin = i;}
      if (b>sum) {//得到新的最优值时,更新最优解
         sum = b; 
         besti = begin; 
         bestj = i;
       }
  }
  return sum;

}

4.0-1背包问题

给定一个物品集合s={1,2,3,…,n},物品i的重量是wi,其价值是vi,背包的容量为W,即最大载重量不超过W。在限定的总重量W内,我们如何选择物品,才能使得物品的总价值最大。

阶段:
i~n,依次处理每一件物品

状态:
背包的容量:0~V

决策:
在第i阶段,在背包容量是V 的情况下,如何处理第i件物品,才能使装入背包中的物品最大。
f[i][v]表示前i件物品(部分或全部)放入一个容量为v的背包可以获得的最大价值。

处理方案:
在W[i]>V的情况下,不装入;否则,根据
max{f[i-1][v],f[i-1][v-w[i]]+c[i]}进行决策
状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-w[i]]+c[i]}。


#include<cstdio>
using namespace std;
const int maxm = 201, maxn = 31;
int m, n;    int w[maxn], c[maxn];   int
f[maxn][maxm]; 
int max(int x,int y)  { x>y?x:y;}    //求x和y最大值
int main(){  
 scanf("%d%d",&m,&n);         //背包容量m和物品数量n   
for (int i = 1; i <= n; i++)     
scanf("%d%d",&w[i],&c[i]);    //每个物品的重量和价值   
for (int i = 1; i <= n; i++)  //
f[i][v]表示前i件物品,总重量不超过v的最优价值       
for (int v = m; v > 0; v--)       
    if (w[i] <= v) 
                    f[i][v] =max(f[i-1][v],f[i-1][v-w[i]]+c[i]);
            else 
                   f[i][v] = f[i-1][v];    
printf("%d",f[n][m]);               // f[n][m]为最优解    
return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值