6.DynamicProgramming

来源:互联网 发布:linux下退出vi 编辑:程序博客网 时间:2024/05/16 18:55

思想:

最优子结构性质:一个问题的最优解包含了子问题的最优解。

重叠子问题性质:在求解问题的过程中反复求解某些子问题。

按照重叠性,以迭代法自底向上的求解问题。

基本步骤:

1.分析问题结构,是否满足最优子结构性质

2.给出最优解所对应的最优值迭代公式

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

求解问题:

一、矩阵连乘问题:

Input:n个矩阵{A1,A2,...,An},其中AiAi+1是可乘的

Output:n个矩阵的乘积的最优计算次序

1.分析问题是否具有最优子结构

假设A(i,j)表示从矩阵ij的最优计算次序,则A(1,n)即为所求。

计算A(1,n)总可以表示为分别计算子问题A(1,k)A(k+1,n)再将这两个中间结果相乘,

如果A(1,k)A(k+1,n)不是最优计算次序,则总可以被替换,使得A(1,n)更优。故原问题具有最优子结构性质。

2.给出最优解所对应的最优值的迭代公式

C(i,j)表示矩阵ij的连乘的计算量


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;public class MatrixMultProblem {public static void main(String[] args) {int[] A = { 4, 6, 5, 8, 15 };MatrixMultProblem test = new MatrixMultProblem();String optOder = test.getTheOptimalCaculatOrder(A);System.out.println(optOder);}/** *  * @param a *            A[i]=第i个矩阵的行数,也即第i-1个矩阵的列数 * @return 最优计算次序,以加括号的方式表示:((A1A2)A3)(A4) */public String getTheOptimalCaculatOrder(int[] a) {int n = a.length - 1;// 表示矩阵个数int[][] c = new int[n][n];// C[i][j]表示第i到j个矩阵连乘的最优花费int[][] p = new int[n][n];// P[i][j]表示第i到j个矩阵连乘的中间矩阵// 初始化,C[i][i]=0for (int i = 0; i < c.length; i++) {c[i][i] = 0;}for (int d = 2; d <= n; d++) {// d表示连乘的矩阵的个数for (int i = 0; i <= n - d; i++) {// 起始矩阵int j = i + d - 1;// 结束矩阵int min = c[i][i] + c[i + 1][j] + a[i] * a[i + 1] * a[j + 1];p[i][j] = i;for (int k = i + 2; k <= j; k++) {// 计算最优的中间矩阵,将矩阵分开int temp = c[i][k - 1] + c[k][j] + a[i] * a[k] * a[j + 1];if (temp < min) {min = temp;p[i][j] = k - 1;}}c[i][j] = min;}}System.out.println("最优值=" + c[0][n - 1]);return getOder(p, 0, n - 1);}/** * 由最优值构造最优解 *  * @param p *            p[i][j]表示第i到j个矩阵连乘的中间矩阵 * @param l *            表示起始矩阵 * @param h *            表示结束矩阵 * @param res *            存储最优计算次序方案 */private String getOder(int[][] p, int l, int h) {// TODO Auto-generated method stubString res = "";if (l >= h) {res += ("A" + (l + 1));return res;}int t = p[l][h];if (l != t)res += ("(");res += getOder(p, l, t);if (l != t)res += (")");if (h != t + 1)res += ("(");res += getOder(p, t + 1, h);if (h != t + 1)res += (")");return res;}private void disp(int[][] a) {// TODO Auto-generated method stubfor (int i = 0; i < a.length; i++) {for (int j = 0; j < a[0].length; j++) {System.out.print(a[i][j] + "\t");}System.out.println();}}}


二、最优二叉查找树的构造:

二叉查找树:中序遍历为有序。最优二叉查找树:按照元素出现的概率,构造二叉查找树使得平均比较次数最少。

Input:有序的键值,及各键值查找的概率

Output:由所有键值构成的平均查找次数最少的二叉查找树

1.分析问题是否具有最优子结构

假设a1,a2,...,an为从小到大排好序的键值,对应的查找概率为p1,p2,...,pnTij为键ai,...,aj构成的最优二叉查找树,对应的平均比较次数为C(i,j)。则T1n为所要求的最优解,C(1,n)为所要求的最优值。若T1n的中间根节点为ak,则T1k-1Tk+1n分别为其左右子树,若T1k-1Tk+1n不是最优二叉查找树,则一定存在比T1n更优的二叉查找树,故根节点ak的左右子树一定也是最优二叉查找树。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;class BTree {char key;BTree lchild;BTree rchild;}public class PlainOptBinarySearchTree {public static void main(String[] args) {char[] key = { 'A', 'B', 'C', 'D' };double[] p = { 0.2, 0.3, 0.1, 0.4 };PlainOptBinarySearchTree obst = new PlainOptBinarySearchTree();BTree t = obst.plaining(key, p);obst.disp(t);}// 中序遍历二叉树tprivate void disp(BTree t) {// TODO Auto-generated method stubif (t != null) {disp(t.lchild);System.out.print(t.key + " ");disp(t.rchild);}}/** * 构造最优二叉查找树方法 *  * @param key *            关键字 * @param p *            各关键字出现的概率 * @return 返回构建的二叉树 */public BTree plaining(char[] key, double[] p) {double[][] C = new double[p.length][p.length];int[][] root = new int[p.length][p.length];// 初始化for (int i = 0; i < p.length; i++) {C[i][i] = p[i];root[i][i] = i;}// 迭代法得到最优值for (int n = 2; n <= p.length; n++) {// 子树节点数nfor (int i = 0; i <= p.length - n; i++) {// 子树第一个节点编号int j = i + n - 1;// 子树最后一个节点编号double sum = 0;for (int k = i; k <= j; k++) {sum += p[k];}double min = 0 + C[i + 1][j];root[i][j] = i;for (int k = i + 1; k < j; k++) {double temp = C[i][k - 1] + C[k + 1][j];if (temp < min) {min = temp;root[i][j] = k;}}double temp = C[i][j - 1];if (temp < min) {min = temp;root[i][j] = j;}C[i][j] = min + sum;}}return getBTree(key, root, 0, p.length - 1);}/** * 递归法生成树 *  * @param key *            关键字 * @param root *            root[i][j]表示i到j这些节点的父节点 * @param i * @param j * @return 返回由root构造的二叉树 */public BTree getBTree(char[] key, int[][] root, int i, int j) {if (i <= j) {int r = root[i][j];BTree t = new BTree();t.key = key[r];t.lchild = getBTree(key, root, i, r - 1);t.rchild = getBTree(key, root, r + 1, j);return t;}return null;}<p>}</p>

三、近似串匹配问题:----还没搞懂

Input:样本串P=p1p2...pm,文本串T=t1t2...tn

Output:所有位置1<=i<=m-n+1,使得titi+1....ti+n-1p1p2...pn至多有k个错配。也叫样本P在文本T中的K近似匹配。

1.分析问题是否具有最优子结构

假设D(i,j)表示样本串P的前i个字符与文本串T的前j个字符的最多差别个数,则D(m,n)即为所求。(似乎不一定满足最优子结构)

2.给出最优解所对应的最优值的迭代公式

对于样本串P的第i个元素pi和文本串T的第j个元素tj,存在两种情况pi=tjpi!=tj

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

四、多段图的最短路径问题:

Input:多段图的段数k,顶点数n,每条边的权重cij

Output:源点到汇点的最短路径及决策过程

1.分析问题是否具有最优子结构

从起点到终点的最短路径包含了该路径上各点到终点的最短路径。

假设C(i)为定点i到汇点n的最短路径长度,则C(1)即为所求的最优值。若C(1)不具最优子结构性质,则子路径C(i)不为最优,此时一定存在比C(1)更优值。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;public class M_GraphShortestProblem {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubint k = 5;int n = 10;int[][] c = new int[10][10];for (int i = 0; i < c.length; i++) {for (int j = 0; j < c.length; j++) {c[i][j] = 10000;}}c[0][1] = 4;c[0][2] = 3;c[0][3] = 3;c[1][4] = 2;c[1][5] = 3;c[2][4] = 2;c[2][5] = 1;c[2][6] = 3;c[3][5] = 2;c[3][6] = 1;c[4][7] = 3;c[4][8] = 4;c[5][7] = 2;c[5][8] = 1;c[6][7] = 6;c[6][8] = 4;c[7][9] = 5;c[8][9] = 3;new M_GraphShortestProblem().getTheShortestPath(c, k, n);}/** *  * @param c *            c[i][j]表示顶点i到j的距离 * @param k *            k表示段的个数 * @param n *            n表示顶点个数 */public void getTheShortestPath(int[][] c, int k, int n) {int[] pre = new int[n];int[] C = new int[n];// 初始化C[n - 1] = 0;int temp = 0;for (int i = n - 2; i >= 0; i--) {C[i] = c[i][i + 1] + C[i + 1];pre[i] = i + 1;for (int j = i + 2; j <= n - 1; j++) {temp = c[i][j] + C[j];if (temp < C[i]) {C[i] = temp;pre[i] = j;}}}System.out.println("最短路径长度为:" + C[0]);System.out.print("0->");dispPath(pre, 0, n - 1);}// 递归得到最短路径private void dispPath(int[] pre, int k, int n) {// TODO Auto-generated method stubif (k >= n - 1) {System.out.print(pre[k]);return;}System.out.print(pre[k] + "->");dispPath(pre, pre[k], n);}}

五、多源最短路径问题Floyd算法:

Input:邻接矩阵w

Output:每个顶点到其他所有顶点的最短路径

1.分析问题是否具有最优子结构

从起点到终点的最短路径包含了该路径上各点到终点的最短路径。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;public class MultiSourseShortestRoutProblem {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubint[][] w = { { 0, 10000, 5, 6 }, { 4, 0, 10000, 10000 },{ 10000, 5, 0, 3 }, { 10000, 2, 10000, 0 } };new MultiSourseShortestRoutProblem().floyd(w);}/** * @param w *            存储各边权重 */public void floyd(int[][] w) {int[][] d1 = new int[w.length][w.length];// 存放上次得到的最短路径int[][] d2 = new int[w.length][w.length];// 存放本次计算的最短路径int[][] path = new int[w.length][w.length];// path[i][j]保存点i到点j的最短路径的最后一个中间节点// 初始化for (int i = 0; i < d1.length; i++) {for (int j = 0; j < d1[0].length; j++) {d1[i][j] = w[i][j];path[i][j] = -1;}}// 动态规划,逐级构造,每次添加一个中间节点for (int k = 0; k < w.length; k++) {// 一次添加一个中间节点,更新最短路径矩阵for (int i = 0; i < w.length; i++) {for (int j = 0; j < w.length; j++) {d2[i][j] = d1[i][j];int temp = d1[i][k] + d1[k][j];if (temp < d1[i][j]) {d2[i][j] = temp;path[i][j] = k;}}}d1 = d2.clone();}System.out.println("所有顶点之间的最短路径长度:");disp(d2);System.out.println("路径信息矩阵:");disp(path);}/** * 输出二维矩阵a *  * @param a */private void disp(int[][] a) {// TODO Auto-generated method stubfor (int i = 0; i < a.length; i++) {for (int j = 0; j < a[0].length; j++) {System.out.print(a[i][j] + "\t");}System.out.println();}}}

六、0\1背包问题:

Inputn个重量为w1,w2,...,wn的物品,价值分别为v1,v2,...,vn;最大载重量为W的背包。

Output:求背包能装下的最大价值量及相应物品。

1.分析问题是否具有最优子结构

不同的问题划分方式会得到不同的问题子结构。

假设由前i个物品组成且背包的总载重为j时的最优解为V[i,j],则原问题的最优解为V[n,W]。如V[n,W]包含物品n,则余下的物品组成的子问题(由前n-1个物品组成,且背包的载重量为W-wn)也必然为最优。

2.给出最优解所对应的最优值的迭代公式

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;public class BeiBaoProblem {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stub// int W = 9;// int n = 4;// int[] w = { 2, 3, 4, 5 };// int[] v = { 3, 4, 5, 7 }; int W = 10; int n = 4; int[] w = { 4, 7, 5, 3 }; int[] v = { 40, 42, 25, 12 };new BeiBaoProblem().getTheMaxValue(n, w, v, W);}/** *  * @param n *            物品数量 * @param w *            物品重量 * @param v *            物品价值量 * @param W *            背包载重量 */public void getTheMaxValue(int n, int[] w, int[] v, int W) {int[][] maxV = new int[n + 1][W + 1];// maxV[i][j]表示i个货物且背包载重为j时能装的最大价值int[] putW = new int[n + 1];// 记录第i件物品是否被装入// 初始化for (int i = 0; i <= n; i++) {maxV[i][0] = 0;}for (int i = 0; i <= W; i++) {maxV[0][i] = 0;}for (int i = 1; i <= n; i++) {// 一次添加一件物品for (int j = 1; j <= W; j++) {// 载重量逐渐增加if (w[i - 1] <= j) {// 背包能装下i物品maxV[i][j] = maxV[i - 1][j];int temp = maxV[i - 1][j - w[i - 1]] + v[i - 1];if (temp > maxV[i][j]) {maxV[i][j] = temp;putW[i] = 1;}} else {maxV[i][j] = maxV[i - 1][j];}}}System.out.println("最大值为:" + maxV[n][W]);showTheW(w, maxV, n, W);}// 如果maxV[i][W]!=maxV[i-1][W],说明第i个物品被加入private void showTheW(int[] w, int[][] maxV, int n, int W) {// TODO Auto-generated method stubSystem.out.println("背包中的物品有:");int t = W;for (int i = n; i > 0; i--) {if (maxV[i][t] != maxV[i - 1][t]) {t = t - w[i - 1];System.out.print(i + "\t");}}}}

七、最长公共子序列问题:

Input:两个字符串str1str2

Ooutput:两个字符串的最长公共子序列

1.分析问题是否具有最优子结构

假设L(i,j)表示字符串str1的前i个字符和str2的前j个字符的最大公共子串长度,则L(m,n)就表示两个字符串的最长公共子序列长度,即为最优值。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;import java.util.Collection;import java.util.HashSet;import java.util.Iterator;import java.util.Set;public class TheLongestCommonSubsequence {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stub// char[] str1 = { 'a', 'c', 'r', 'd', 'k', 'f', 'a' };// char[] str2 = { 'd', 'c', 'e', 'd', 'l', 'f', 'a' };char[] str1 = { 'a', 'b', 'a', 'b', 'a', 'b', 'c', 'd', 'e' };char[] str2 = { 'a', 'b', 'b', 'b', 'b', 'b' };int m = 9, n = 6;new TheLongestCommonSubsequence().getTheLongestComSubseq(str1, str2, m,n);}/** *  * @param str1 *            字符串str1长度m * @param str2 *            字符串str2长度n * @param m * @param n */public void getTheLongestComSubseq(char[] str1, char[] str2, int m, int n) {int[][] L = new int[m + 1][n + 1];int[][] last = new int[m + 1][n + 1];// 初始化for (int i = 0; i <= m; i++) {L[i][0] = 0;}for (int i = 0; i <= n; i++) {L[0][i] = 0;}for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {if (str1[i - 1] == str2[j - 1]) {L[i][j] = L[i - 1][j - 1] + 1;last[i][j] = 1;// 表示str1的第i个字符和str2的第j个字符相等} else if (L[i - 1][j] > L[i][j - 1]) {L[i][j] = L[i - 1][j];last[i][j] = 2;// 表示str1的第i个字符不在最长公共串里} else if (L[i - 1][j] == L[i][j - 1]) {L[i][j] = L[i][j - 1];last[i][j] = 3;// 表示str2的第j个字符不在最长公共串里且str1的第i个字符不在最长公共串里} else {L[i][j] = L[i][j - 1];last[i][j] = 4;// 表示str2的第j个字符不在最长公共串里}}}System.out.println(String.valueOf(str1) + " 和 " + String.valueOf(str2)+ "的最大公共子串长度为:" + L[m][n]);Set<String> res = new HashSet<String>();getStr(str1, last, m, n, "", res);System.out.println("最大公共子串为:" + res.toString());}private void getStr(char[] str1, int[][] last, int m, int n, String s,Set<String> res) {// TODO Auto-generated method stubif (m > 0 && n > 0) {if (last[m][n] == 1) {s = str1[m - 1] + s;getStr(str1, last, m - 1, n - 1, s, res);} else if (last[m][n] == 2) {// 表示str1的该字符不在最长公共串里getStr(str1, last, m - 1, n, s, res);} else if (last[m][n] == 3) {// 表示str2的该字符不在最长公共串里且str1的第i个字符不在最长公共串里getStr(str1, last, m, n - 1, s, res);getStr(str1, last, m - 1, n, s, res);} else if (last[m][n] == 4) {// 表示str2的该字符不在最长公共串里getStr(str1, last, m, n - 1, s, res);}} else {res.add(s);}}}

八、最长的单调递增子序列

Input:一个长度为n的整数序列A

Output:该序列A中最长的单调递增子序列

1.分析问题是否具有最优子结构

假设L(i,j)表示序列A中前i个元素构成的小于j的单调递增子序列长度,则L中的最大值即为所求的最优值。若最长单调递增子序列为B长度为k,且B(k)A中第i个元素,则L(i,B(k))为最优值,若B(k-1)A中第j个元素,则L(j,B(k-1))也一定是子问题(序列A的前j个元素的单调递增子序列)的最优值。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;public class TheLongestIncreaseSubsuquence {public static void main(String[] args) {int[] A = { 1, 2, 5, 3, 9, 4, 5, 6 };new TheLongestIncreaseSubsuquence().LIS(A);}/** * 求序列A的最长递增子序列 *  * @param A */public void LIS(int[] A) {int[] L = new int[A.length];int[] p = new int[A.length];// 初始化L[0] = 1;for (int i = 0; i < A.length; i++) {p[i] = -1;}// 自底向上方法,求的最优值for (int i = 1; i < A.length; i++) {L[i] = 1;for (int k = 0; k < i; k++) {if (A[k] < A[i]) {if (L[i] < L[k] + 1) {L[i] = L[k] + 1;p[i] = k;}} else if (A[k] == A[i]) {if (L[i] < L[k]) {L[i] = L[k];p[i] = k;}}}}// 构造最优解int ll = 1;int li = 0;for (int i = 0; i < A.length; i++) {if (ll < L[i]) {ll = L[i];li = i;}System.out.print(A[i]);}System.out.println("的最长递增子序列长度为:" + ll);System.out.println("最长递增子序列为:");dispSubSequence(A, p, li);}private void dispSubSequence(int[] A, int[] p, int li) {// TODO Auto-generated method stubif (li > -1) {dispSubSequence(A, p, p[li]);System.out.print(A[li]);}}}

九、最大子段和

Input:一个长度为n的整数序列A

Output:该序列A的最大子段和及该最大子段序列

1.分析问题是否具有最优子结构

假设V(i)表示序列A的前i个元素的最大字段和且包含第i个元素,则V中的最大值即为所求。如果把状态设为这样,则问题是具有最优子结构的。

2.给出最优解所对应的最优值的迭代公式

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;public class TheBiggestSubSum {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubint[] A = { -3, 2, -1, 4, -5, 2, 1, 2, 3, -4, 5, -6, 7, -8 };new TheBiggestSubSum().theBiggestSubSum(A);}public void theBiggestSubSum(int[] A) {int[] V = new int[A.length];int[] p = new int[A.length];// 初始化V[0] = A[0];// 构造数组Vfor (int i = 1; i < A.length; i++) {if (V[i - 1] <= 0) {V[i] = A[i];p[i] = i;} else {V[i] = V[i - 1] + A[i];p[i] = p[i - 1];}}// 找到最大值int maxSum = 0;int maxi = 0;for (int i = 0; i < A.length; i++) {if (maxSum < V[i]) {maxSum = V[i];maxi = i;}}System.out.print("序列");for (int i = 0; i < A.length; i++) {System.out.print(A[i] + ",");}System.out.println("的最大子段和为:" + maxSum);System.out.print("最大子段为:");for (int i = p[maxi]; i <= maxi; i++) {System.out.print(A[i] + ",");}}}


0 0
原创粉丝点击