- 动态规划(dynamic programming)是通过组合子问题解而解决整个问题的一种方法。分治法是将问题划分为一些独立的子问题,递归地求解各子问题,然后合并子问题解而得到原始问题解。2者区别在于分治法划分的子问题是独立的,而动态规划子问题是不独立的。动态规划做法是对子问题求解一次,将解存入一张表中,从而避免了重复计算。
- 动态规划常应用于最优化问题。
- 算法分为4步
1.描述最优解的结构
2.递归定义最优解值
3.自底向上求得最优解的值
4.由计算结果构造一个最优解 - 下面就对算法导论上面4个最优化问题进行简单分析
1.装配线调度
2.矩阵链乘法
3.最长公共子序列
4.最优二叉查找树
// 装配线调度
1.问题描述
每一条装配线上有N个装配站,每个装配站的工作时间不一样。同样一共有M个装配线。同一条装配线中站与站之间传输时间为0,不同装配线不同站之间传输时间不为0。即每条装配线上每个站都有自己传输到其它线的时间代价。开始有一个进入装配线的时间代价,最后有一个退出装配线的时间代价。
2.解决思路- 描述最优解结构
设 f1,j,f2,j,f3,j,...,fm,j 为到站点 S1,j,S2,j,S3,j,...,Sm,j 的最小代价,则 f1,j+1 可以由子问题求出。 - 递归定义最优解
fi,j 是子问题最优解,即到达 Si,j 的最小代价。
fi,j=min(fi,j−1,fk,j−1+transformk,j−1),1<=k<=M且k≠i - 自底向上求最优解(代码)
- 构造最优解(代码)
- 描述最优解结构
- 矩阵链乘法
1.问题描述
矩阵序列ABCDEFG相乘,不同的结合律会导致矩阵相乘次数的不同。比如 A1∗2,B2∗3,C3,1 ,A和B先乘再和C乘,则一共9次。而B和C先乘,再和A乘,则一共10次。
2.解决思路
m[i,j]=min(m[i,k]+m[k+1,j]+pi−1pkpj) 最长公共子序列
1.问题描述
例如X={A,B,C,D,A,B},Y={A,C,A,D,B},则最长公共序列是{A,C,D,B}长度为4。
2.解决思路
1. 描述一个最长公共子序列
定理:X={x1,x2,..,xm},Y={y1,y2,…,yn}为2个序列,设Z={z1,z2,…,zk}是X和Y的任意一个LCS。
1)如果xm=yn,则xm=yn=zk,且Zk-1是Xm-1Yn-1的一个LCS
2)如果xm ≠ yn,那么zk ≠ xm,则Z是Xm-1和Y的一个LCS
3)如果xm ≠ yn,那么zk ≠ yn,则Z是Yn-1和X的一个LCS
2. 递归解
c[i,j]=⎧⎩⎨⎪⎪0c[i−1,j−1]+1max(c[i−1,j],c[i,j−1])i=0||j=0x[i]=y[j]x[i]≠y[j]最优二叉查找树
- 问题描述
给定由n个关键字组成的序列,且有序。从这些关键字中构造一棵二叉查找树。对每个关键字 ki ,一次搜索 ki 的概率是 pi 。某些搜索不在序列中,因此有n+1个虚拟键 di 不在序列当中,一次搜索 di 的概率是 qi 。我们想构造一个总期望最小的二叉查找树。 - 解决思路(和矩阵链乘法相似)
为描述最优二叉查找树的子结构,首先考虑它的子树。字数必定包含连续范围内的关键字{ ki,ki+1,...,kj }。必定含有虚拟键{ di−1,di,...,dj }作为叶子。
如果一个最优二叉查找树有包含{ ki,ki+1,...,kj }的子树,那么这个子树对于关键字{ ki,ki+1,...,kj }以及虚拟键{ di−1,di,...,dj }的子问题必定是最优的。
利用最优子结构来说明可以根据子问题的最优解来构造原问题的最优解。
递归解:
e[i,j]={qi−1当j=i−1min{e[i,r−1]+e[r+1,j]+w(i,j)}
其中 w(i,j)=∑jl=ipl+∑jl=i−1ql
- 问题描述
//装配线调度
import java.util.Stack;
class Path
{
int id;
Path preStation;
public Path(int id) {
// TODO Auto-generated constructor stub
this.id = id;
}
public void show()
{
Stack<Integer> stack = new Stack<>();
Path path = this;
while(path.id != -1)
{
stack.add(path.id);
path = path.preStation;
}
while(!stack.isEmpty()){
System.out.print(stack.pop()+" ");
}
System.out.println();
}
}
public class AssemblyLineDispach {
final int[][] stationTime = new int[][]
{
{1,2000,400},
{2,300,4},
{3,10000,10}
};
final int[][] transformSpend = new int[][]
{
{2,3},
{1,2},
{1,2}
};
final int[] beginSpend = new int[]{100,3,6};
final int[] endSpend = new int[]{1,2,3};
private int getMinId(int[] array)
{
int min = Integer.MAX_VALUE;
int id = -1;
for(int i = 0;i<array.length;++i){
// min = array[i] < min ? array[i] : min;
if(array[i] < min)
{
min = array[i];
id = i;
}
}
return id;
}
private void forwardAlgo()
{
int m = stationTime.length;
int n = stationTime[0].length;
Path[] optimalPath = new Path[m];
// Path sentienal = new Path(-1);
for(int i=0;i<optimalPath.length;i++)
{
Path sentienal = new Path(-1);
optimalPath[i] = sentienal;
}
int[] dp = new int[m];
//Path[] path_s = new Path[m];
for(int i=0;i<m;i++)//dp[i][0]:到达 i j 的最小代价
{
dp[i] = beginSpend[i];
Path p = new Path(i);
p.preStation = optimalPath[i];
optimalPath[i] = p;
}
int j = 0;
for(j = 1;j<n;j++)
{
for(int i=0;i<m;i++){dp[i] += stationTime[i][j-1];}
int minValueId = -1;
int minValue = Integer.MAX_VALUE;
for(int i=0;i<m;i++)
{
if(dp[i]+transformSpend[i][j-1] < minValue)
{
minValue = dp[i]+transformSpend[i][j-1];
minValueId = i;
}
}
Path[] paths = new Path[m];
for(int i=0;i<m;i++)
{
int min = dp[i];
int minId = i;
if(min > minValue)
{
min = minValue;
minId = minValueId;
}
dp[i] = min;
Path path = new Path(i);
path.preStation = optimalPath[minId];
paths[i] = path;
}
for(int i = 0;i<paths.length;i++)
{
optimalPath[i] = paths[i];
}
}
for(int i=0;i<m;i++)
{
dp[i] += stationTime[i][j-1];
dp[i] += endSpend[i];
}
int optimalPathId = getMinId(dp);
System.out.println(dp[optimalPathId]);
optimalPath[optimalPathId].show();
// g
}
class Path_
{
int id;
Path_ next;
public Path_(int id) {
// TODO Auto-generated constructor stub
this.id = id;
}
public void show()
{
Path_ temp = this;
while(temp != null)
{
System.out.print(temp.id+" ");
temp = temp.next;
}
System.out.println();
}
}
private void backwardAlgo()
{
int m = stationTime.length;
int n = stationTime[0].length;
Path_[] optimalPath = new Path_[m];
int[] dp = new int[m];//dp i,j i,j 到结尾的代价
for(int i=0;i<m;i++)
{
dp[i] = endSpend[i];
optimalPath[i] = new Path_(i);
}
//for(int i=0;i<m;i++){temp[i] = optimalPath[i];}
for(int j=n-2;j>=0;j--)
{
for(int i=0;i<m;i++){dp[i] += stationTime[i][j+1];}
int minValueId = -1;
int minValue = Integer.MAX_VALUE;
for(int i=0;i<m;i++)
{
if(dp[i] < minValue)
{
minValue = dp[i];
minValueId = i;
}
}
Path_[] temp = new Path_[m];
for(int i=0;i<m;i++)
{
int minId = i;
if(minValue + transformSpend[i][j] < dp[i])
{
dp[i] = minValue + transformSpend[i][j];
minId = minValueId;
}
temp[i] = new Path_(i);
temp[i].next = optimalPath[minId];
}
for(int i=0;i<m;i++)
{
optimalPath[i] = temp[i];
}
}
for(int i=0;i<m;i++)
{
dp[i] += stationTime[i][0];
dp[i] += beginSpend[i];
}
int resultId = getMinId(dp);
System.out.println(dp[resultId]);
optimalPath[resultId].show();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
AssemblyLineDispach test = new AssemblyLineDispach();
test.forwardAlgo();
test.backwardAlgo();
}
}
//矩阵连乘法
import java.util.ArrayList;
import java.util.List;
class Matrix<T>
{
int row;
int col;
public Matrix(int row, int col) {
// TODO Auto-generated constructor stub
this.row = row;
this.col = col;
}
}
public class OptimalMatrix {
class MatrixInfor
{
int row;
int col;
int iterNum;
int optimalSpanIndex;
public MatrixInfor(int row, int col, int iterNum, int optimalSpanIndex) {
// TODO Auto-generated constructor stub
this.row = row;
this.col = col;
this.iterNum = iterNum;
this.optimalSpanIndex = optimalSpanIndex;
}
}
MatrixInfor[][] dp = null;
public void matrixChainOrder(List<Matrix> chain) //dp[i j] = min(dp[i k] + dp[k+1 j] + s[k]*s[q])
{
int m = chain.size();
dp = new MatrixInfor[m][m];
for(int i=0;i<m;i++)
{
dp[i][i] = new MatrixInfor(chain.get(i).row, chain.get(i).col, 0, -1);
}
for(int span = 1;span<m;span++)
{
for(int i=0;i<m-span;i++)
{
int j = i+span;
int minSpend = Integer.MAX_VALUE;
int optimalSpanIndex = -1;
for(int k=i;k<j;k++)
{
int iterTime = getIterTime(dp[i][k],dp[k+1][j]) + dp[i][k].iterNum + dp[k+1][j].iterNum;
if(iterTime < minSpend)
{
minSpend = iterTime;
optimalSpanIndex = k;
}
}
dp[i][j] = new MatrixInfor(dp[i][i].row, dp[j][j].col, minSpend, optimalSpanIndex);
}
}
System.out.println(dp[0][m-1].iterNum);
optimalMatrixPrint(0, m-1);
}
private int getIterTime(MatrixInfor dp2, MatrixInfor dp3) {
// TODO Auto-generated method stub
if(dp2.col != dp3.row){
System.out.println("col row is not equal");
return -1;
}
return dp2.row * dp2.col * dp3.col;
}
public void optimalMatrixPrint(int begin, int end)
{
if(begin == end)
{
System.out.print("A");
return;
}
System.out.print("(");
optimalMatrixPrint(begin, dp[begin][end].optimalSpanIndex);
optimalMatrixPrint(dp[begin][end].optimalSpanIndex+1, end);
System.out.print(")");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
OptimalMatrix test = new OptimalMatrix();
List<Matrix> chain = new ArrayList<>();
chain.add(new Matrix(30, 35));
chain.add(new Matrix(35, 15));
chain.add(new Matrix(15, 5));
chain.add(new Matrix(5, 10));
chain.add(new Matrix(10, 20));
chain.add(new Matrix(20, 25));
test.matrixChainOrder(chain );
}
}
//最长公共序列
public class LongCommonSequence {
int[][] dp = null;
public void LCS(String s1, String s2)
{
int m = s1.length(), n = s2.length();
dp = new int[m+1][n+1];
for(int i=0;i<=m;i++)
{
dp[i][0] = 0;
}
for(int i=0;i<=n;i++)
{
dp[0][i] = 0;
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(s1.charAt(i-1) == s2.charAt(j-1))
{
dp[i][j] = dp[i-1][j-1] + 1;
}else
{
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
System.out.println(dp[m][n]);
printRoute(s1, s2,m, n);
}
public void printRoute(String s1, String s2, int m, int n)
{
if(m == 0 || n == 0){
return;
}
if(s1.charAt(m-1) == s2.charAt(n-1))
{
printRoute(s1,s2, m-1, n-1);
System.out.print(s1.charAt(m-1)+" ");
}else
if(dp[m][n-1] > dp[m-1][n])
{
printRoute(s1,s2, m, n-1);
}else{
printRoute(s1,s2, m-1, n);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LongCommonSequence lcs = new LongCommonSequence();
lcs.LCS("ABCBDAB", "BDCABA");
}
}
//最优二叉查找树
class TreeNode
{
TreeNode left;
TreeNode right;
String val;
public TreeNode(String val) {
// TODO Auto-generated constructor stub
this.val = val;
}
}
public class OptimalBST {
class Node
{
float val;
TreeNode node;
public Node(float val, TreeNode node) {
// TODO Auto-generated constructor stub
this.val = val;
this.node = node;
}
}
public void obst(float[] p, float[] q, int n)
{
Node[][] dp_e = new Node[n+2][n+1];
float[][] dp_w = new float[n+2][n+1];
for(int i=1;i<=n+1;i++)
{
dp_e[i][i-1] = new Node(q[i-1], new TreeNode("q"+(i-1)));
dp_w[i][i-1] = q[i-1];
}
for(int span = 0;span<n;span++)
{
for(int i=1;i<n-span+1;i++)
{
int j = i+span;
dp_w[i][j] = dp_w[i][j-1] + p[j] + q[j];
float minValue = Float.MAX_VALUE;
int minValueIndex = -1;
for(int r = i;r<=j;r++)
{
float val = dp_e[i][r-1].val + dp_e[r+1][j].val + dp_w[i][j];
if(val < minValue)
{
minValue = val;//crucial
minValueIndex = r;//
}
}
TreeNode optimalNode = new TreeNode("p"+(minValueIndex));
optimalNode.left = dp_e[i][minValueIndex-1].node;
optimalNode.right = dp_e[minValueIndex+1][j].node;
dp_e[i][j] = new Node(minValue, optimalNode);
}
}
System.out.println(dp_e[1][n].val);
print(dp_e[1][n].node);
}
private void print(TreeNode node) {
// TODO Auto-generated method stub
if(node == null)
{
return;
}
print(node.left);
System.out.print(node.val + " ");
print(node.right);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
OptimalBST obst = new OptimalBST();
obst.obst(new float[]{0,0.04f,0.06f,0.08f,0.02f,0.10f,0.12f,0.14f}, new float[]{0.06f,0.06f,0.06f,0.06f,0.05f,0.05f,0.05f,0.05f}, 7);
}
}