1. 程式人生 > >(動態規劃DP)面試題:求陣列中兩個數的最大差值(只能下標大的減去下標小的)符合無後效性

(動態規劃DP)面試題:求陣列中兩個數的最大差值(只能下標大的減去下標小的)符合無後效性

空間複雜度優化演算法

void sovle_maxSub_Dp_OptimalSpace(int *a, int n){
 int S=0;
 int max_value=INT_MIN;
 int max_index=0;
 for(int i=n-2;i>=1;i--){
  S=S<0?a[i]-a[i+1]:a[i]-a[i+1]+S;
  if(max_value<S){
   max_value=S;
   max_index=i;
  }
 }
 cout<<"最大差值為:"<<max_value<<",對應的兩個元素為:"<<endl;
 cout<<"陣列中第"<<max_index<<"個元素:"<<a[max_index]<<endl;
 for(int i=max_index+1;i<=n;i++){
  if(a[max_index]-a[i]==max_value){
   cout<<"陣列中第"<<i<<"個元素:"<<a[i]<<endl;
   break;
  }
 }
}


當時頭大,只寫了窮舉法 O(N2)的複雜度,肯定還有簡單的方法呀,只是當時不知怎地就是想不起來緊張的要死告訴面試官暫時另一個辦法想不起來了

回來一看又是程式設計之美里的題目,其實我在暑假的時候已經看過了那個題目可惜可惜。現在在這裡寫出來給自己一個經驗教訓!

動態規劃演算法O(N)

給定一個整數陣列,a[1],a[2],...,a[n],每一個元素a[i]可以和它右邊的(a[i+1],a[i+2],...,a[n])元素做差,求這個陣列中最大的差值,例如a={0,3,9,1,3,5}這個陣列最大的差值就是9-1=8;(此為小減大,面試給的是大減小)

利用DP的思想,定義S[i]為a[i]與其右邊元素之差的最大值,那麼狀態轉移方程為:
s[i]=a[i]-a[i+1]      if s[i+1]<0   (表明a[i+1]是從i+1到n所有元素中最小的一個)
s[i]=s[i+1]+a[i]-a[i+1]  if s[i+1]>=0  

動態規劃
一,動態規劃三要素:階段,狀態,決策。
如果把動態規劃的求解過程看成一個工廠的生產線,階段就是生產某個商品的不同的環節,狀態就是工件當前的形態,決策就是對工件的操作。顯然不同階段是對產品的一個前面各個狀態的小結,有一個個的小結構成了最終的整個生產線。每個狀態間又有關聯(下一個狀態是由上一個狀態做了某個決策後產生的)。
下面舉個例子:
要生產一批雪糕,在這個過程中要分好多環節:購買牛奶,對牛奶提純處理,放入工廠加工,加工後的商品要包裝,包裝後就去銷售……,這樣沒個環節就可以看做是一個階段;產品在不同的時候有不同的狀態,剛開始時只是白白的牛奶,進入生產後做成了各種造型,從冷凍庫拿出來後就變成雪糕(由液態變成固態)。每個形態就是一個狀態,那從液態變成固態經過了冰凍這一操作,這個操作就是一個決策。
一個狀態經過一個決策變成了另外一個狀態,這個過程就是狀態轉移,用來描述狀態轉移的方程就是

狀態轉移方程
經過這個例子相信大家對動態規劃有所瞭解了吧。
下面在說說我對動態規劃的另外一個理解:
用圖論知識理解動態規劃:把動態規劃中的狀態抽象成一個點,在有直接關聯的狀態間連一條有向邊,狀態轉移的代價就是邊上的權。這樣就形成了一個有向無環圖AOE網(為什麼無環呢?往下看)。對這個圖進行拓撲排序,刪除一個邊後同時出現入度為0的狀態在同一階段。這樣對圖求最優路徑就是動態規劃問題的求解。
二,動態規劃的適用範圍
動態規劃用於解決多階段決策最優化問題,但是不是所有的最優化問題都可以用動態規劃解答呢?
一般在題目中出現求最優解的問題就要考慮動態規劃了,但是否可以用還要滿足兩個條件:
1最優子結構(最優化原理)
2無後效性
最優化原理在下面的最短路徑問題中有詳細的解答;
什麼是無後效性呢?
就是說在狀態i求解時用到狀態j而狀態j就解有用到狀態k…..狀態N。
而求狀態N時有用到了狀態i這樣求解狀態的過程形成了環就沒法用動態規劃解答了,這也是上面用圖論理解動態規劃中形成的圖無環的原因。
也就是說當前狀態是前面狀態的完美總結,現在與過去無關。。。
當然,有是換一個劃分狀態或階段的方法就滿足無後效性了,這樣的問題仍然可以用動態規劃解。
三,動態規劃解決問題的一般思路。
拿到多階段決策最優化問題後,第一步要判斷這個問題是否可以用動態規劃解決,如果不能就要考慮搜尋或貪心了。當卻定問題可以用動態規劃後,就要用下面介紹的方法解決問題了:
(1)模型匹配法:
         最先考慮的就是這個方法了。挖掘問題的本質,如果發現問題是自己熟悉的某個基本的模型,就直接套用,但要小心其中的一些小的變動,現在考題辦都是基本模型的變形套用時要小心條件,三思而後行。這些基本模型在先面的分類中將一一介紹。
(2)三要素法
仔細分析問題嘗試著確定動態規劃的三要素,不同問題的卻定方向不同:
先確定階段的問題:數塔問題,和走路問題(詳見解題報告)
先確定狀態的問題:大多數都是先確定狀態的。
先確定決策的問題:揹包問題。(詳見解題報告)
一般都是先從比較明顯的地方入手,至於怎麼知道哪個明顯就是經驗問題了,多做題就會發現。
(3)尋找規律法:
這個方法很簡單,耐心推幾組資料後,看他們的規律,總結規律間的共性,有點貪心的意思。
(4)邊界條件法
         找到問題的邊界條件,然後考慮邊界條件與它的領接狀態之間的關係。這個方法也很起效。
(5)放寬約束和增加約束
         這個思想是在陳啟鋒的論文裡看到的,具體內容就是給問題增加一些條件或刪除一些條件使問題變的清晰。

動態規劃是解決多決策問題最優化問題的一種良好演算法,典型的DP應用就是揹包問題,影象壓縮問題等,DP可以決解決貪心演算法和分而治之問題中一些難以解決的問題