1. 程式人生 > >動態規劃法--求陣列中最大子集合的和

動態規劃法--求陣列中最大子集合的和

例題:給定一個數組int[] a = {-9,1,3,5,-1,7,-5,3,1};

計算陣列中連續的最大和以及出現的位置

 輸出:下標1到5位連續的最大和為15

首先看到這種題目,我的第一反應·就是用氣泡排序的思想去做:

public class zuoye{
    public static void main(String[] args){
	int[] a = {-9,1,3,5,-1,7,-5,3,1};
	int max=a[0];
	int tempValue=0;
	int x=0,y=0;
	for(int i=0;i<a.length-1;i++){
	    tempValue=a[i];
	    for(int j=i+1;j<a.length;j++){
		tempValue+=a[j];
		if(max<tempValue){
			max=tempValue;
			x=i;
			y=j;
		}
	    }
	}
	System.out.println(x+" "+y+" "+max);
    }
}

具體過程如圖所示一步步往下走

程式執行結果如下圖:



後來我覺得用動態規劃法做從時間空間複雜度上來說就有明顯的區別。

用氣泡排序的思想做,時間複雜度(最壞的情況)為O(n^2

若果用動態規劃法要做時間複雜度只有O(n)

首先來說下動態規劃法的概念(難以理解,我們還是看下)

動態規劃求解的基本步驟:

能採用動態規劃求解的問題一般要具有3個性質:

(1)最優化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最優化原理。

(2)無後效性:即某階段的狀態一定確立,就不受這個狀態以後決策的影響,也就是說,某狀態以後的過程不會影響以前的狀態,只與當前狀態有關。

(3)有重疊子問題:即子問題之間是不獨立的,一個字問題在下一階段的決策中可能被多次使用到(該性質不是動態規劃的必要條件,但是如果沒有這條性質,動態規劃演算法同其他演算法相比就不具備優勢)

動態規劃演算法有一定自己的模式,一般要經歷如下幾個步驟:

(1)劃分階段:按照問題的時間或空間特徵,把問題分為若干個階段,在劃分階段注重劃分後的階段一定要是有序或者是可排序的,否則問題就無法求解。

(2)確定狀態和狀態變數:將問題發展到各個階段時所處於的各種客觀情況用不同的狀態表示出來。當然,狀態的選擇要滿足無後效性。

(3)確定決策並寫出狀態轉移方程:因為決策和狀態轉移方程有著天然的聯絡。狀態轉移就是根據上一階段的狀態和決策來匯出本階段的狀態。所以決定了決策,狀態轉移方程也就可以寫出。

但事實上常常是反過來做,根據相鄰的兩個階段的狀態之間的關係來確定決策方法和轉移方程。

(4)尋找邊界條件:給出的狀態轉移方程是一個遞推式,需要一個遞推的終止條件或者邊界條件。

動態規劃法的基本思想:

將待求解的基本問題分為若干個子問題,按照順序求解子階段,前一個問題的解,為後一子問題的求解提供有用的資訊。在求解任何問題時,列出各種可能的區域性解,通過決策保留那些可能達到最優的區域性解,丟棄其他區域性解。依次解決各個子問題,最後一個子問題就是初始化的解。

動態規劃採用的基本辦法:

為了節約重複求相同子問題的時間,引入一個數組或者一組變數,不管它們是否對最終解有用,把所有子問題的解存於該陣列或者這組變數中。


我覺得最好理解的為上面的基本方法:

/*如果用函式f(i)表示以第i個數字結尾的子陣列的最大和,那麼我們需要求出max(f[0...n])。 
我們可以給出如下遞迴公式求f(i) 
     |-- array[i] 如果i==0或者f(i-1)<0 
f(i)=| 
     |-- f(i-1) + array[i] 如果f(i-1)>0 
這個公式的意義: 
   當以第(i-1)個數字為結尾的子陣列中所有數字的和f(i-1)小於0時,如果把這個負數和第i個數相加,得到的結果反而不第i個數本身還要小,所以這種情況下最大子陣列和是第i個數本身。 
 如果以第(i-1)個數字為結尾的子陣列中所有數字的和f(i-1)大於0,與第i個數累加就得到了以第i個數結尾的子陣列中所有數字的和。 
*/  

public class array {
	public static void main(String[] args){
	int[] array = {-9,1,3,5,-1,7,-5,3,1};
	int len=array.length;
	int[] c=new int[len];//引入一個數組
	int max = -1000;//用來記錄陣列c[]中的最大值  
	int start = 0;//記錄陣列中子陣列的最大和的開始位置  
	int end = 0;//記錄陣列中子陣列的最大和的結束位置  
	int tmp = 0;//中間變數
	c[0] = array[0];
	for (int i = 1; i < len; ++i)
	{
		if (c[i - 1] > 0)
		{
			c[i] = c[i - 1] + array[i];
		}
		else
		{
			c[i] = array[i];
			tmp = i;
		}
		if (c[i] > max)
		{
			max = c[i];
			start = tmp;
			end = i;
		}
	}
	System.out.println(start+"~"+end+"Max is:"+max);
   }
}
上面程式碼就是用動態規劃法來求解的利用了一個數組C來儲存臨時產生的值,只用了一層for迴圈,時間複雜度為O(n)。大大的加快了速度。

結果是一樣的,分析過程就由你們自己去分析了^*^....