程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 四邊形不等式優化_石子合並問題_C++,不等式_c

四邊形不等式優化_石子合並問題_C++,不等式_c

編輯:C++入門知識

四邊形不等式優化_石子合並問題_C++,不等式_c


  在動態規劃中,經常遇到形如下式的狀態轉移方程:

    m(i,j)=min{m(i,k-1),m(k,j)}+w(i,j)(i≤k≤j)(min也可以改為max)

  上述的m(i,j)表示區間[i,j]上的某個最優值。w(i,j)表示在轉移時需要額外付出的代價。該方程的時間復雜度為O(N3)

   

  下面我們通過四邊形不等式來優化上述方程,首先介紹什麼是“區間包含的單調性”和“四邊形不等式”

    1、區間包含的單調性:如果對於 i≤i'<j≤j',有 w(i',j)≤w(i,j'),那麼說明w具有區間包含的單調性。(可以形象理解為如果小區間包含於大區間中,那麼小區間的w值不超過大區間的w值)

    2、四邊形不等式:如果對於 i≤i'<j≤j',有 w(i,j)+w(i',j')≤w(i',j)+w(i,j'),我們稱函數w滿足四邊形不等式。(可以形象理解為兩個交錯區間的w的和不超過小區間與大區間的w的和)

 

  下面給出兩個定理:

    1、如果上述的 w 函數同時滿足區間包含單調性和四邊形不等式性質,那麼函數 m 也滿足四邊形不等式性質

       我們再定義 s(i,j) 表示 m(i,j) 取得最優值時對應的下標(即 i≤k≤j 時,k 處的 w 值最大,則 s(i,j)=k)。此時有如下定理

    2、假如 m(i,j) 滿足四邊形不等式,那麼 s(i,j) 單調,即 s(i,j)≤s(i,j+1)≤s(i+1,j+1)。

 

  好了,有了上述的兩個定理後,我們發現如果w函數滿足區間包含單調性和四邊形不等式性質,那麼有 s(i,j-1)≤s(i,j)≤s(i+1,j) 。

  即原來的狀態轉移方程可以改寫為下式:

     m(i,j)=min{m(i,k-1),m(k,j)}+w(i,j)(s(i,j-1)≤k≤s(i+1,j))(min也可以改為max)

  由於這個狀態轉移方程枚舉的是區間長度 L=j-i,而 s(i,j-1) 和 s(i+1,j) 的長度為 L-1,是之前已經計算過的,可以直接調用。

  不僅如此,區間的長度最多有n個,對於固定的長度 L,不同的狀態也有 n 個,故時間復雜度為 O(N^2),而原來的時間復雜度為 O(N^3),實現了優化!

  今後只需要根據方程的形式以及 w 函數是否滿足兩條性質即可考慮使用四邊形不等式來優化了。

 

  以上描述狀態用 m(i,j),後文用的 dp[i][j],所代表含意是相同的,特此說明。

  以石子合並問題為例。

  例如有6堆石子,每堆石子數依次為3 4 6 5 4 2

  因為是相鄰石子合並,所以不能用貪心(每次取最小的兩堆合並),只能用動歸。(注意:環形石子的話,必須要考慮最後一堆和第一堆的合並。)

  例如:一個合並石子的方案:

    第一次合並 3 4 6 5 4 2 ->7

    第二次合並 7 6 5 4 2 ->13

    第三次合並 13 5 4 2 ->6

    第四次合並 13 5 6 ->11

    第五次合並 13 11 ->24

  總得分=7+6+11+13+24=61 顯然,比貪心法得出的合並方案(得分:62)更優。

  

  動歸分析類似矩陣連乘等問題,得出遞推方程:

    設 dp[i][j] 表示第 i 到第 j 堆石子合並的最優值,sum[i][j] 表示第 i 到第 j 堆石子的總數量。

    (可以在計算開始先做一遍求所有的 sum[i],表示求出所有第1堆到第i堆的總數量。則 sum[i][j]=sum[j]-sum[i]。這樣計算比較快。)

  那麼就有狀態轉移公式:

      

    這裡 i<=k<j

  普通解法需要 O(n^3)。下面使用四邊形不等式進行優化。

  首先判斷是否符合區間單調性和四邊形不等式。

     i  i'    j    j'

    3 4 6 5 4 2

  單調性:

    w[i',j] = 4+6+5=15 w[i,j'] =3+4+6+5+4+2=24

  故w[i',j] <= w[i,j'] 滿足單調性

  四邊形不等式:

    w[i,j] + w[i',j'] = (3+4+6+5) + (4+6+5+4+2) = 18+21 = 39

    w[i',j] + w[i,j'] = (4+6+5) + (3+4+6+5+4+2) = 15 + 24 = 39

    故 w[i,j] + w[i',j'] <= w[i',j] + w[i,j']

  故石子合並可利用四邊形不等式進行優化。

 

  利用四邊形不等式,將原遞推方程的狀態轉移數量進行壓縮(即縮小了k的取值范圍)。

  令 s[i][j]=min{k | dp[i][j] = dp[i][k-1] + dp[k][j] + w[i][j]},即計算出 dp[i][j] 時的最優的 k 值(本例中尋優為取最小)

  也可以稱為最優決策時的 k 值。由於決策 s 具有單調性,因此狀態轉移方程中的 k 的取值范圍可修改為 :

    s[i,j-1] <= s[i,j] <= s[i+1,j]

    邊界:s[i,i] = i

  因為 s[i,j] 的值在 m[i,j] 取得最優值時,保存和更新,因此 s[i,j-1] 和 s[i+1,j] 都在計算 dp[i][j-1] 以及 dp[i+1][j] 的時候已經計算出來了。

  因此,s[i][j] 即 k 的取值范圍很容易確定。

  根據改進後的狀態方程,以及 s[i][j] 的定義方程,可以很快的計算出所有狀態的值。計算過程可以如下表所示(類似於矩陣連乘的打表)。

  狀態表(如果是環形石子合並,需要打2n*2n的表)

    3 4 6 5 4 2

  

  例如:

    計算dp[1][3],由於s[1][2]=1,s[2][3]=2,則k值的取值范圍是1<=k<=2

    則,dp[1][3]=min{dp(1,1)+dp(2,3)+13, dp(1,2)+dp(3,3)+13}=min{10+13, 7+13}=20,將其填到狀態表。同時,由於取最優值的k等於2,則將其填到s表。

    同理,可以計算其他狀態表和s表中的值。

      dp[2][4]=min{dp(2,2)+dp(3,4)+15, dp(2,3)+dp(4,4)+15}=min{11+15, 10+15}=25

      k=3

    從表中可以看出,當計算dp[2][5]的時候,由於s[ i,j-1]=s[ 2,4]=3,s[ i+1,j]=s[3,5]=3,此時k的取值范圍已經限定為只有一個,大幅縮短了尋找最優解的時間。

 

  這裡給出程序代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 const int N=205;
 6 const int INF=0x7fffffff;
 7 int n;
 8 int a[N],sum[N],dp[N][N],s[N][N];
 9 void f(); 
10 int main()
11 {
12      while(~scanf("%d",&n))
13      {
14          sum[0]=0;
15         for (int i=1;i<=n;i++)
16         {
17             scanf("%d",&a[i]);
18             sum[i]=sum[i-1]+a[i];
19         }
20         f();
21         printf("%d\n",dp[1][n]);
22      }
23      return 0;
24 
25 }
26 void f()
27 {
28      for (int i=1;i<=n;i++) dp[i][i]=0,s[i][i]=i;
29      for (int r=1;r<n;r++)
30      {
31          for (int i=1;i<n;i++)
32         {
33             int j=i+r;
34             if(j>n) break;
35             dp[i][j]=INF;
36             for (int k=s[i][j-1];k<=s[i+1][j];k++)
37             {
38                 if(dp[i][j]>dp[i][k]+dp[k+1][j])
39                 {
40                     dp[i][j]=dp[i][k]+dp[k+1][j];
41                     s[i][j]=k;
42                 }
43             }
44             dp[i][j]+=sum[j]-sum[i-1];
45         }
46     }
47 }

 

 

 

本文轉載自網易博客:

    http://blog.163.com/dqx_wl/blog/static/2396821452015111133052112/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved