動態規劃問題,大致可以通過以下四部分進行解決:
- 劃分階段:按照問題的時間或空間特徵,把問題分爲若干個子階段。(劃分後的子階段一定要是有序的或者是可排序的,否則問題就無法求解。)
- 狀態表示:將問題發展到各個階段時所處於的各種客觀情況用不同的狀態表示出來。(要滿足無後效性。)
- 狀態轉移:狀態轉移就是根據上一階段的狀態和決策來導出本階段的狀態。
- 確定邊界:狀態轉移方程是一個遞推式,需要一個遞推的終止條件或邊界條件。
只有當每個子問題都是離散的,不依賴於其他子問題時,動態規劃才管用 。
最大子序列
最長公共子序列(Longest Common Subsequence):兩個序列公共子序列中的最長者,下面簡稱LCS,可能出現下面兩種比較複雜的情況:
1.可能有多個
2.可能有歧義
步驟:
- 首先判斷兩個字符是否相等,如果相等,根據上一個字符匹配的情況的最終值去生成該網格的值。
- 如果不相等,根據網格的生成情況,獲取該網格的前一個或者上一個的最終值進行比較大小後填充。
以此類推,得到狀態轉移公式:
實現:
/**
* @Classname LargestSubsequence
* @Description 動態規劃——最大子序列
* @Date 2019/8/12 14:03
* @Created zzf
*/
public class LargestSubsequence {
public static void main(String[] args) {
String str1 = "ADADVANTAGE";
String str2 = "EDDUCATIONAL";
int max = 0;
char[] chars1 = str1.toCharArray();
char[] chars2 = str2.toCharArray();
//生成網格數組
int[][] arrs = new int[chars1.length + 1][chars2.length + 1];
//核心
for (int i = 1; i <= chars1.length; i++) {
for (int j = 1; j <= chars2.length; j++) {
//如果字符匹配成功
if (chars1[i - 1] == chars2[j - 1]) {
//當前網格的值爲上一層網格的前一個值+1。
print_arr(arrs);
arrs[i][j] = arrs[i - 1][j - 1] + 1;
System.out.println("*********************************************");
print_arr(arrs);
System.out.println("*********************************************");
} else {
print_arr(arrs);
//不相等時取左邊或者上邊兩個比較大者
arrs[i][j] = arrs[i - 1][j] > arrs[i][j - 1] ? arrs[i - 1][j] : arrs[i][j - 1];
System.out.println("---------------------------------------------");
print_arr(arrs);
System.out.println("---------------------------------------------");
}
}
}
print_arr(arrs);
System.out.println("最大公共序列值:" + arrs[chars1.length][chars2.length]);
}
public static void print_arr(int[][] arrs) {
for (int i = 0; i < arrs.length; i++) {
for (int j = 0; j < arrs[0].length; j++) {
System.out.print(arrs[i][j] + " ");
}
System.out.println("");
}
}
}
揹包問題
步驟:
- 首先判斷該商品是否能夠放進該空間大小的揹包
- 如果不能:獲取上次的最優值
- 如果能:判斷上次的最優值與(本件商品的值)+最優值[(該大小的揹包-本件商品的大小)],根據比較結果進行更新當前網格的最優值
以此類推,得到狀態轉移公式:
實現:
/**
* @Classname Knapsack
* @Description 揹包問題
* @Date 2019/8/12 15:02
* @Created zzf
*/
public class Knapsack {
public static void main(String[] args) {
//最大存儲
int MaxS = 13;
int weight[] = {3, 4, 5, 6};
int value[] = {4, 5, 6, 7};
int number = weight.length;
//生成網格
int[][] V = new int[number + 1][MaxS + 1];
//商品的下標
for (int i = 1; i <= number; i++) {
//揹包可存儲大小從1~MaxC最大格
for (int j = 1; j <= MaxS; j++) {
//判斷該物品是否能夠存儲進該大小的揹包
if (weight[i - 1] <= j) {
//判斷上次存儲在該大小的網格的價值是否比(該商品價格)+最優存儲((該網格-該商品大小))要小
if (V[i - 1][j] < V[i - 1][j - weight[i - 1]] + value[i - 1]) {
//更新該網格的最優存儲價值
V[i][j] = V[i - 1][j - weight[i - 1]] + value[i - 1];
} else {
//說明此次存儲的網格最優存儲任然爲上次存儲的網格值(不更新)
V[i][j] = V[i - 1][j];
}
} else {
//不可以
//該網格的最優存儲爲上一次存儲的值
V[i][j] = V[i - 1][j];
}
}
}
for (int i = 1; i <= number; i++) {
for (int j = 1; j <= MaxS; j++) {
System.out.print(V[i][j] + "\t");
}
System.out.println();
}
System.out.println("最存儲是:" + V[number][MaxS]);
}
}
總結
- 每種動態規劃解決方案都涉及網格
- 每個單元格都是一個子問題,而狀態轉移公式就是計算該單元格的最優解
- 問題可分解成離散子問題時,可以用動態規劃解決