【算法导论】第15章动态规划
2012-06-20 20:46
190 查看
1、问题引入
和分治法一样,动态规划是通过组合子问题的解而解决整个问题的。分治法是指将问题划分成一些独立的子问题,递归求各个子问题,然后合并子问题的解而得到原问题的解。而动态规划适用于子问题不独立的情况,也就是各个子问题包含公共的“子子问题”,在这种情况下,分治法将不便于求解,而动态规划算法将对每个“子子问题”只求一次解,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。
动态规划通常应用于最优化问题,此类问题可能有多种可行解,每个解有一个值,而我们希望找出一个具有最优值的解,称这样的解为该问题的“一个”最优解(而不是“确定的”最优解),因为可能存在多个取最优值的解。
动态规划的设计可以分成如下4个步骤:
(1)描述最优解的结构;
(2)递归定义最优解的值;
(3)按自底向上的方式计算最优解的值;
(4)由计算结果构造一个最优解。
接下来将利用动态规划方法求解几个问题:装配线调度问题、矩阵链乘法问题、求最长的公共子序列问题、构造最优二叉查找树问题。
2、装配线调度
2.1 问题描述
某汽车工厂有2个装配线,每个装配线有n 个装配站(按顺序编号1~n )标记为Si,j,表示第i个装配线的第j个装配站,两个装配线的对应的装配站执行相同的功能,但所用的时间可能不同。经过第i条流水线(i=1,2)的第j 个装配站所花的时间为a[i][j]。从第i条流水线的第j 个装配站移到第j+1个装配站的时间可以忽略,而移到另外一个流水线的下一个装配站则需要一定的时间t[i][j]。汽车进入流水线需要花时间,进入装配线1需要时间e[1],进入装配线2需要时间e[2]; 汽车出流水线时需要花时间,出装配线1需要时间x[1],出装配线2需要时间x[2] 。汽车的装配需要按顺序经过所有装配站。
现在已知装配时间a[i][j] 、转移时间t[i][j]、进入装配线的时间e[i]、出装配线的时间x[i],要求输出装配一辆汽车所需要的最短时间,以及经过的装配站。
2.2 求解过程
(1)最优子结构
对于装配线问题,推理如下:一条经过装配线S1,j的最快路径,必定是经过装配线1或2上的装配站j-1.因此通过S1,j的最快路径只能是以下二者之一:
(a)通过装配站S1,j-1的最快路径,然后通过装配站S1,j;
(b)通过装配站S2,j-1的最快路径,从装配线2移动到装配线1,然后通过装配站S1,j。
对于装配线2,有类似结论。
(2)一个递归的解
最终目标是确定通过工厂所有的装配线的最快时间,记为f*。设f[i][j]为一个汽车从起点到装配站Si,j的最快可能时间。汽车必然是经过装配线1或2最终到达装配站n,然后到达工厂的出口。即:
f*=min(f[1]
+x[1] , f[2]
+x[2])
要对f1
和f2
进行推理可以运用步骤1。
初始化:f[1][1]=e[1]+a[1][1]
f[2][1]=e[2]+a[2][1]
计算f[i][j],其中j=2,3...,n;i=1,2
f[1][j]=min( f[1][j-1]+a[1][j] , f[2][j-1]+t[2][j-1]+a[1][j])
f[2][j]=min( f[2][j-1]+a[2][j] , f[1][j-1]+t[1][j-1]+a[2][j])
为了便于跟踪最优解的构造过程,定义l[i][j]:i为装配线的编号,i=1,2 ,j表示装配站j-1被通过装配站Si,j的最快路线所使用,j=2,3,...,n;
(3)自底向上计算最优解的值
用算法描述如下:
2.3、具体实现代码如下:
View Code
附:另外一个用动态规划求左右二叉查找树的程序:/article/5040489.html
6、有向无环图的单源最短路径长度(较简单)
附:用动态规划求有向无环图的单源最短路径:/article/5040483.html
7、0-1背包问题
附:用动态规划求0-1背包问题:/article/5040485.html
8、数塔
附:用动态规划求数塔问题:/article/5040484.html
9、参考资料:
(1):http://blog.csdn.net/xiaoyjy/article/details/2420861
(2):算法导论
(3):c编程
和分治法一样,动态规划是通过组合子问题的解而解决整个问题的。分治法是指将问题划分成一些独立的子问题,递归求各个子问题,然后合并子问题的解而得到原问题的解。而动态规划适用于子问题不独立的情况,也就是各个子问题包含公共的“子子问题”,在这种情况下,分治法将不便于求解,而动态规划算法将对每个“子子问题”只求一次解,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。
动态规划通常应用于最优化问题,此类问题可能有多种可行解,每个解有一个值,而我们希望找出一个具有最优值的解,称这样的解为该问题的“一个”最优解(而不是“确定的”最优解),因为可能存在多个取最优值的解。
动态规划的设计可以分成如下4个步骤:
(1)描述最优解的结构;
(2)递归定义最优解的值;
(3)按自底向上的方式计算最优解的值;
(4)由计算结果构造一个最优解。
接下来将利用动态规划方法求解几个问题:装配线调度问题、矩阵链乘法问题、求最长的公共子序列问题、构造最优二叉查找树问题。
2、装配线调度
2.1 问题描述
某汽车工厂有2个装配线,每个装配线有n 个装配站(按顺序编号1~n )标记为Si,j,表示第i个装配线的第j个装配站,两个装配线的对应的装配站执行相同的功能,但所用的时间可能不同。经过第i条流水线(i=1,2)的第j 个装配站所花的时间为a[i][j]。从第i条流水线的第j 个装配站移到第j+1个装配站的时间可以忽略,而移到另外一个流水线的下一个装配站则需要一定的时间t[i][j]。汽车进入流水线需要花时间,进入装配线1需要时间e[1],进入装配线2需要时间e[2]; 汽车出流水线时需要花时间,出装配线1需要时间x[1],出装配线2需要时间x[2] 。汽车的装配需要按顺序经过所有装配站。
现在已知装配时间a[i][j] 、转移时间t[i][j]、进入装配线的时间e[i]、出装配线的时间x[i],要求输出装配一辆汽车所需要的最短时间,以及经过的装配站。
2.2 求解过程
(1)最优子结构
对于装配线问题,推理如下:一条经过装配线S1,j的最快路径,必定是经过装配线1或2上的装配站j-1.因此通过S1,j的最快路径只能是以下二者之一:
(a)通过装配站S1,j-1的最快路径,然后通过装配站S1,j;
(b)通过装配站S2,j-1的最快路径,从装配线2移动到装配线1,然后通过装配站S1,j。
对于装配线2,有类似结论。
(2)一个递归的解
最终目标是确定通过工厂所有的装配线的最快时间,记为f*。设f[i][j]为一个汽车从起点到装配站Si,j的最快可能时间。汽车必然是经过装配线1或2最终到达装配站n,然后到达工厂的出口。即:
f*=min(f[1]
+x[1] , f[2]
+x[2])
要对f1
和f2
进行推理可以运用步骤1。
初始化:f[1][1]=e[1]+a[1][1]
f[2][1]=e[2]+a[2][1]
计算f[i][j],其中j=2,3...,n;i=1,2
f[1][j]=min( f[1][j-1]+a[1][j] , f[2][j-1]+t[2][j-1]+a[1][j])
f[2][j]=min( f[2][j-1]+a[2][j] , f[1][j-1]+t[1][j-1]+a[2][j])
为了便于跟踪最优解的构造过程,定义l[i][j]:i为装配线的编号,i=1,2 ,j表示装配站j-1被通过装配站Si,j的最快路线所使用,j=2,3,...,n;
(3)自底向上计算最优解的值
用算法描述如下:
int fastestWay f[1][1]=e[1]+a[1][1]; f[2][1]=e[2]+a[2][1]; for(j=2;j<n;j++) { if(f[1][j-1]+a[1][j]<=f[2][j-1]+t[2][j-1]+a[1][j]) { f[1][j]=f[1][j-1]+a[1][j]; l[1][j]=1; } else { f[1][j]=f[2][j-1]+t[2][j-1]+a[1][j]; l[1][j]=2; } if(f[2][j-1]+a[2][j]<=f[1][j-1]+t[1][j-1]+a[2][j]) { f[2][j]=f[2][j-1]+a[2][j]; l[2][j]=2; } else { f[2][j]=f[1][j-1]+t[1][j-1]+a[2][j]; l[2][j]=1; } }
2.3、具体实现代码如下:
View Code
#include<stdio.h> #include<malloc.h> #define max 9999 #define n 5 double optimalBst(double p[],double q[],int root[][n+1]) { int i,j,l,r; double t; double w[n+2][n+1]; double e[n+2][n+1]; for(i=1;i<=n+1;i++) { e[i][i-1]=q[i-1]; w[i][i-1]=q[i-1]; } for(l=1;l<=n;l++) { for(i=1;i<=n-l+1;i++) { j=i+l-1; e[i][j]=max; w[i][j]=w[i][j-1]+p[j]+q[j]; for(r=i;r<=j;r++) { t=e[i][r-1]+e[r+1][j]+w[i][j]; if(t<e[i][j]) { e[i][j]=t; root[i][j]=r; } } } } return(e[1] ); } void printBst(int root[][n+1],int i,int j) { int k; if(i<=j) { printf("%d " ,root[i][j]); k=root[i][j]; printBst(root,i,k-1); printBst(root,k+1,j); } } void main() { int i; double k; int root[n+1][n+1]; double p[n+1]={0,0.15,0.10,0.05,0.10,0.20}; double q[n+1]={0.05,0.10,0.05,0.05,0.05,0.10}; k=optimalBst(p,q,root); printf("最小期望搜索代价为:%f\n",k); printf("最优二叉查找树的中序遍历结果为:\n"); printBst(root,1,n); }
附:另外一个用动态规划求左右二叉查找树的程序:/article/5040489.html
6、有向无环图的单源最短路径长度(较简单)
附:用动态规划求有向无环图的单源最短路径:/article/5040483.html
7、0-1背包问题
附:用动态规划求0-1背包问题:/article/5040485.html
8、数塔
附:用动态规划求数塔问题:/article/5040484.html
9、参考资料:
(1):http://blog.csdn.net/xiaoyjy/article/details/2420861
(2):算法导论
(3):c编程
相关文章推荐
- 算法导论 第四部分——基本数据结构——第15章:动态规划:背包问题
- 动态规划之钢条切割问题自底向上发的实现(算法导论第15章)
- 算法导论 第15章 动态规划:矩阵链乘法
- 算法导论 第15章 动态规划:15.1钢条切割
- 流水线调度问题实现(动态规划基础---------问题取自算法导论)
- 动态规划之钢条切割(算法导论)
- 动态规划之最长公共子序列(算法导论)
- 动态规划之钢条切割(算法导论)
- 算法导论之动态规划 签约棒球自由球员
- 动态规划(算法导论学习笔记)
- 装配线调度问题 算法导论动态规划P194
- 动态规划原理(算法导论)
- 动态规划原理(算法导论)
- 算法导论学习笔记(十三):动态规划(三):01背包问题
- 算法导论之动态规划之最大子数组
- C++实现算法导论十五章动态规划之钢条分割问题
- 高效算法之动态规划(第15章)
- 算法导论之动态规划:矩阵链相乘
- 算法导论之动态规划:矩阵链相乘
- 动态规划(3)——算法导论(18)