第四章 动态规划

动态规划算法的有效性依赖于问题本身所具有的两个重要性质:
1.最优子结构
2.重叠子问题

设计动态规划法的步骤:
找出最优解的性质,并刻画其结构特征;
递归地定义最优值(写出动态规划方程);
以自底向上的方式计算出最优值;
根据计算最优值时得到的信息,构造一个最优解。
矩阵连乘问题
分析:

A[1:n]的计算量= A[1:k]的计算量+A[k+1:n]的计算量+A[1:k]和A[k+1:n]相乘的计算量

m[i][j]给出了最优值,最优断开位置为k:(i<j)

m[i][j]=0 (i=j)
若将对应于m[i][ j]的断开位置k记为s[i][ j],在计算出最优值m[i][ j]后,可递归的由s[i][ j]构造出相应的最优解


#include<iostream>
#include<cstdio>
using namespace std;

int p[51]={50,10,40,30,5,20,15};
int m[51][51];                                         //状态,记录子问题的最优值
int s[51][51];                                          //记录子问题的最优解
void MatrixChain (int n){
  for (int i=1;i<=n;i++) 
        m[i][i] = 0;
  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];
	  s[i][j] = i;                                    //最优解的初值
    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;}
  }
   }
}
//计算矩阵连乘积最优解的递归算法
void TraceBack(int i, int j) {
	if(i==j) cout<<"A "<< i;
	else
	{
		cout<<"(";
		TraceBack(i,s[i][j]);
		TraceBack(s[i][j]+1,j);
		cout<<")";
	}
}


int main(){
  void MatrixChain(6); 
  cout<<m[2][5];
  void TraceBack(1,6);
}


最长公共子序列
最优子结构的分析:
设序列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的最长公共子序列。


#include<iostream>
#include<cstdio>
#define NUM 100
using namespace std;

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;
  for (i = 1; i <= m; i++) c[i][0] = 0;
  for (i = 1; i <= n; i++) c[0][i] = 0;
  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);
}


int main(){
char x[]={A,B,C,B,D,A,B};
char y[]={B,D,C,A,B,A};
LCSLength (6, 7, x,y);
LCS(6,7,x);

}


最大字段和
当所有整数均为负值时定义其最大子段和为0。

#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) {               //得到新的最优值时, 
	  besti = begin; 
	  bestj = i;
	}
  }
  return sum;
}


背包问题
有N种物品和一个容量为V的背包
第i种物品的费用是w[i],价值是c[i]。

完全背包的状态转移方程可以等价地变形成这种形式:
f[i][v]=max{f[i-1][v],f[i][v-w[i]]+c[i]},

#include<cstdio>
using namespace std;
const int maxm = 201, maxn = 31;int m, n,w[maxn], c[maxn], f[maxn][maxm]; 
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 = 1; v <= m; v++)
            if (v < w[i])  f[i][v] = f[i-1][v];
            else
      if (f[i-1][v] > f[i][v-w[i]]+c[i])  f[i][v] = f[i-1][v]; //不装入第i件物品
                else f[i][v] = f[i][v-w[i]]+c[i]; //多次装入第i件物品
    printf("max=%d",f[n][m]);          // f[n][m]为最优解
    return 0;}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值