動態規劃的一個簡單例項java
如果一個問題可以分為多個子問題,這若干個子問題如果是相互獨立的,那麼我們可以使用分治的思想解決,如果分解的子問題之間並不是獨立的,那麼就可以使用動態規劃來解決了。
動態規劃原理:
一個最優策略的決策過程中產生的子問題的子策略必然也是最優的,簡單的一個說明就是,最終的策略最優,必然是在前一個最優策略的基礎上做出的最優策略。
動態規劃的一般步驟:
1)描述最優解的結構
2)對最優解的值進行遞迴定義
3)按照自底向上的方法計算最優解的值
4)根據3)計算出的結果構造一個最優解
一個例子:
有兩條生產線1,2,標記為i,每個生產線有n道工序,標記為j,每條生產線上的第j道工序的功能相同,但是消耗的時間不同,當產品進入生產線之後,可以在任意一道工序j完成之後將產品轉移到另一條生產線的第j+1道工序,轉換線路會消耗時間Tij,表示在第i條生產線上完成了第j道工序之後轉移到另一條生產線所消耗的時間。ei表示進入第i條生產線所消耗的時間,xi表示從第i條生產線離開時消耗的時間。畫圖表示為:
其中:
entraA是進入第1條生產線消耗的時間
entraB是進入第2條生產線消耗的時間
aij表示第i條生產線的第j道工序消耗的時間
tij表示從第i條生產線的第j道工序轉移到另一條生產線的第k+1道工序過程中消耗的時間
exitA表示離開第1條生產線消耗的時間
exitB表示離開第2條生產線消耗的時間
分析:
1 如果j=1,那麼只有一條路徑,我們可以比較entraA和entraB的大小即可,如果j>=2,有兩種可能,一是產品直接從同一條生產線的前一道工序過來,進入了第j道工序,二是產品是從另一條生產線轉移過來,這個過程花費了tij-1的時間。但是已經確定的是從產品進入生產線 一直到出了第j-1道工序必定是最快的路徑。這是我們遞推的假設,我們就這樣分析出了子問題,即,要求第j道工序的最快路徑,我們可以先求第j-1道工序的最快路徑。
2 定義表示式
fi[j]表示一個產品從起點進入生產線一直到第i條生產線的第j道工序的最快時間
f*表示全程的最快路徑,問題可以表示為如下方程:
f* = min(f1[n]+exitA,f2[n]+exitB)
n為總共的工序
初始化條件為:
f1[1]=entraA+a1,1 (1)
f2[1]=entraB+a2,1
(2)
當j>=2時,我們就需要考慮1中分析的兩種情況了
f1[j]=min(f1[j-1]+a1,j,f2[j-1]+t2,j-1+a1,j) (3)
f2[j]=min(f2[j-1]+a2,j,f1[j-1]+t1,j-1+a2,j)
(4)
java實現:
/**
* @param a 每道工序消耗的時間
* @param t 轉移到另一條生產性消耗的時間
* @param line 用於記錄最優線路
* @param entraA 進入生產線1的時間
* @param entraB 進入生產線2的時間
* @param exitA 離開生產線1消耗的時間
* @param exitB 離開生產線2消耗的時間
* @param n 工序的總數
*/
public static void fastWayTime(int[][] a, int[][] t, int[][] line, int entraA, int entraB, int exitA, int exitB, int n) {
int minTime;
int[][] f = new int[2][3];
f[0][0] = entraA + a[0][0]; //(1)的表示 初始化
f[1][0] = entraB + a[1][0]; //(2)的表示
for(int j = 1;j<n;j++) {
if(f[0][j-1] + a[0][j] <= f[1][j-1] + t[1][j-1] + a[0][j]) { //(3)
f[0][j] = f[0][j-1] + a[0][j];
line[0][j] = 0; //line的第0位不記錄生產線,因為它只能從起點進入第一道工序
} else {
f[0][j] = f[1][j-1]+t[1][j-1]+a[0][j];
line[0][j] = 1; //line的第0位不記錄生產線,因為它只能從起點進入第一道工序
}
if(f[1][j-1] + a[1][j] <=f[0][j-1]+t[0][j-1]+a[1][j]) { //(4)
f[1][j] = f[1][j-1] + a[1][j];
line[1][j] = 1; //line的第0位不記錄生產線,因為它只能從起點進入第一道工序
} else {
f[1][j] = f[0][j-1] +t[0][j-1] +a[1][j];
line[1][j] = 0; //line的第0位不記錄生產線,因為它只能從起點進入第一道工序
}
}
if(f[0][n-1]+exitA <= f[1][n-1] +exitB) { //f陣列中記錄了各條線路的各道工序的最短時間,因此只需要比較最後一道工序就行了
minTime = f[0][n-1] + exitA;
} else {
minTime = f[1][n-1] + exitB;
}
System.out.println("最快路徑耗時: "+minTime);
fastWayPath(line, lastStation, n);
}
public static void fastWayPath(int[][] line, int lastStation, int n) {
int i = lastStation;
Stack<Integer> path = new Stack<Integer>();
path.push(i);
for(int j=n;j>1;j--) {
i = line[i][j-1]; //line記錄的是第j道工序的最優解的前一道工序是從哪條線路過來的,因此必須從後往前讀取
path.push(i);
}
int k=1;
while(!path.isEmpty()&& k<=n) {
System.out.println("線路"+(path.pop()+1)+": 工序"+k);
k++;
}
}
public static void main(String[] args) {
int[][] a = {{4,3,2},{2,2,3}};
int entraA = 2;
int entraB = 1;
int exitA = 2;
int exitB = 3;
int[][] t = {{4,1},{3,2}};
int n = 3;
int[][] line = new int[2][3];
fastWayTime(a, t, line, entraA, entraB, exitA, exitB, n);
}