1. 程式人生 > >動態規劃的一個簡單例項java

動態規劃的一個簡單例項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);

	}