关于动态规划的经典算法,如多段图最短路径问题、TSP问题、最长公共子序列问题等。
动态规划
经典算法
数塔问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static int Tower (int [][] d) { int n = d.length; int [][] maxAdd = new int [n][n]; int [][] path = new int [n][n]; int i, j; for (j = 0 ; j < n; j++) { maxAdd[n - 1 ][j] = d[n - 1 ][j]; } for (i = n - 2 ; i >= 0 ; i--) { for (j = 0 ; j <= i; j++) { if (maxAdd[i + 1 ][j] > maxAdd[i + 1 ][j + 1 ]) { maxAdd[i][j] = d[i][j] + maxAdd[i + 1 ][j]; path[i][j] = j; } else { maxAdd[i][j] = d[i][j] + maxAdd[i + 1 ][j + 1 ]; path[i][j] = j + 1 ; } } } System.out.println("路径:" + d[0 ][0 ]); j = path[0 ][0 ]; for (i = 1 ; i < n; i++) { System.out.println("-->" + d[i][j]); j = path[i][j]; } System.out.println(Arrays.deepToString(maxAdd)); return maxAdd[0 ][0 ]; }
多段图最短路径问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static int BackPath (Graph graph) { int n = graph.getVnum(); int i, j, temp; int [] cost = new int [n]; int [] path = new int [n]; for (i = 1 ; i < n; i++) { cost[i] = MAX; path[i] = -1 ; } cost[0 ] = 0 ; path[0 ] = -1 ; for (j = 1 ; j < n; j++) { for (i = j - 1 ; i >= 0 ; i--) { if (graph.getMatrix()[i][j] >= 0 ) if (graph.getMatrix()[i][j] + cost[i] < cost[j]) { cost[j] = graph.getMatrix()[i][j] + cost[i]; path[j] = i; } } } System.out.println("-----" + (n - 1 )); i = n - 1 ; while (path[i] >= 0 ) { System.out.println("-----" + path[i]); i = path[i]; } System.out.println(Arrays.toString(cost)); return cost[n - 1 ]; }
多源点最短路径问题(Floyd算法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static int [][] FloydAlgorithm(Graph graph) { int i, j, k; int n = graph.getVnum(); int [][] dist = new int [n][n]; for (i = 0 ; i < n; i++) { for (j = 0 ; j < n; j++) { dist[i][j] = graph.getMatrix()[i][j]; } } for (k = 0 ; k < n; k++) { for (i = 0 ; i < n; i++) { for (j = 0 ; j < n; j++) { if (i != j && i != k && k != j) if (dist[i][k] + dist[k][j] < dist[i][j]) dist[i][j] = dist[i][k] + dist[k][j]; } } } return dist; }
TSP问题(待解决)
最长递增子序列问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public static int IncreaseOrder (int [] a) { int i, j, k, index; int n = a.length; int [] L = new int [n]; int [][] x = new int [n][n]; for (i = 0 ; i < n; i++) { L[i] = 1 ; x[i][0 ] = a[i]; } System.out.println("初始化完成" ); for (int [] item : x) { System.out.println(Arrays.toString(item)); } for (i = 1 ; i < n; i++) { int max = 1 ; for (j = i - 1 ; j >= 0 ; j--) { if ((a[j] < a[i]) && (max < L[j] + 1 )) { max = L[j] + 1 ; L[i] = max; for (k = 0 ; k < max - 1 ; k++) { x[i][k] = x[j][k]; } x[i][max - 1 ] = a[i]; } } } for (index = 0 , i = 1 ; i < n; i++) { if (L[index] < L[i]) index = i; } System.out.println("最长子序列:" ); for (i = 0 ; i < L[index]; i++) { System.out.println(x[index][i]); } for (int [] item : x) { System.out.println(Arrays.toString(item)); } return L[index]; }
最长公共子序列问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 private static int CommonOrder (char [] x, char [] y) { int i, j, k; int n = y.length, m = x.length; char [] Z = new char [Math.max(n, m)]; int [][] DP = new int [m + 1 ][n + 1 ]; int [][] S = new int [m + 1 ][n + 1 ]; for (j = 0 ; j <= n; j++) DP[0 ][j] = 0 ; for (i = 0 ; i <= m; i++) DP[i][0 ] = 0 ; for (i = 1 ; i <= m; i++) for (j = 1 ; j <= n; j++) if (x[i - 1 ] == y[j - 1 ]) { DP[i][j] = DP[i - 1 ][j - 1 ] + 1 ; S[i][j] = 1 ; } else if (DP[i][j - 1 ] >= DP[i - 1 ][j]) { DP[i][j] = DP[i][j - 1 ]; S[i][j] = 2 ; } else { DP[i][j] = DP[i - 1 ][j]; S[i][j] = 3 ; } i = m; j = n; k = DP[m][n]; while (i > 0 && j > 0 ) { if (S[i][j] == 1 ) { Z[k] = x[i - 1 ]; k--; i--; j--; } else if (S[i][j] == 2 ) { j--; } else { i--; } } for (k = 1 ; k <= DP[m][n]; k++) { System.out.println(Z[k]); } return DP[m][n]; }
0/1背包问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static int KnapSack (int [] w, int [] v, int C) { int i, j; int n = w.length; int [] x = new int [n]; int [][] V = new int [n + 1 ][C + 1 ]; for (i = 0 ; i <= n; i++) V[i][0 ] = 0 ; for (j = 0 ; j <= C; j++) V[0 ][j] = 0 ; for (i = 1 ; i <= n; i++) for (j = 1 ; j <= C; j++) if (j < w[i - 1 ]) V[i][j] = V[i - 1 ][j]; else V[i][j] = Math.max(V[i - 1 ][j], V[i - 1 ][j - w[i - 1 ]] + v[i - 1 ]); for (j = C, i = n; i > 0 ; i--) { if (V[i][j] > V[i - 1 ][j]) { x[i - 1 ] = 1 ; j = j - w[i - 1 ]; } else x[i - 1 ] = 0 ; } System.out.println(Arrays.toString(x)); return V[n][C]; }
最优二叉搜索树(待解决)
近似串匹配问题(待理解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static int ASM (char [] P, char [] T) { int i, j; int m = P.length, n = T.length; int [][] D = new int [m + 1 ][n + 1 ]; for (j = 1 ; j <= n; j++) D[0 ][j] = j; for (i = 0 ; i <= m; i++) D[i][0 ] = i; for (j = 1 ; j <= n; j++) { for (i = 1 ; i <= m; i++) { if (P[i - 1 ] == T[j - 1 ]) D[i][j] = Math.min(Math.min(D[i - 1 ][j - 1 ], D[i - 1 ][j] + 1 ), D[i][j - 1 ] + 1 ); else D[i][j] = Math.min(Math.min(D[i - 1 ][j - 1 ] + 1 , D[i - 1 ][j] + 1 ), D[i][j - 1 ] + 1 ); } } return D[m][n]; }
练习
以序列X={a,b,c,b,d,b}
和Y={a,c,b,b,a,b,d,b,b}
为例
长度矩阵为:
0
0
0
0
0
0
0
0
0
0
0
a
0
b
0
c
0
b
0
d
0
b
0
表格的第一行、第一列全部初始化为0,表示空字符串与任何字符串的最长公共子序列长度均为0
从第一个空格开始遍历表格
若所在行和列的字符不相同,则在它上方和左侧的两个数字中选取较大的一个填入(DP[i][j] = max{DP[i-1][j], DP[i][j-1]}
)
若所在行和列的字符相同,则将它左上方的数字+1填入(DP[i][j] = DP[i-1][j-1] + 1
)
在处理上述矩阵的同时,可定义另一个格式相同的矩阵用来记录状态,成为状态矩阵
情况①,两字符相同,则填入1
情况②,两字符不同,且长度矩阵中左侧数字>=上方数字,则填入2
情况③,两字符不同,且长度矩阵中左侧数字<上方数字,则填入3
回溯时,从状态矩阵的最右下角元素开始
遇到1,向左上方回溯
遇到2,向左侧回溯
遇到3,向上方回溯
回溯至最左上角元素后,经过的路线中所有1所在位置对应的字符组成的字符串即为最长公共子序列