• 沒有找到結果。

Dynamic Programming (2)

N/A
N/A
Protected

Academic year: 2022

Share "Dynamic Programming (2)"

Copied!
38
0
0

加載中.... (立即查看全文)

全文

(1)

Dynamic Programming (2)

by music960633

(2)

課程內容 課程內容

• LIS

• LCS

• 空間優化

• 背包問題

• 換零錢問題

(3)

LIS LIS

• Longest Increasing Subsequence ( 最長遞增子序列 )

• 子序列:從原本序列中挑幾個數字出來,不改變前後順序而產生 的序列

• ex: arr[] = {1,5,2,4,6,3} ,則 {1,6,3} 、 {5,2,4,3} 皆是 arr 的子 序列

• LIS :所有遞增的子序列中,最長的子序列,以上面的例子來說

, {1,5,2,4,6,3} 的 LIS 為 {1,2,4,6} ( 可能不唯一 )

• 本題只需要求出最長子序列的長度即可

(4)

LIS LIS

• 定義狀態

• f(n) 為從 arr[1] 到 arr[n] 中取出數字,且以 arr[n] 做結尾的最大遞增 子序列長度

• ex: arr[] = {1,5,2,4,6,3} , 則 f(1~6) = {1,2,2,3,4,3}

• 狀態轉移

• 若 i<n 且 arr[i]<arr[n] ,則可以將以 arr[i] 為結尾的最大遞增子序列 接上 arr[n] ,形成一個長度為 f(i)+1 的子序列

• f(n) 為試過所有 i 之後的最大值

• f(n)=max(f(i))+1, for all i<n and arr[i]<arr[n]

(5)

LIS LIS

• 最後答案

• max(f(i)), i=1 to N

• 空間複雜度 ( 狀態數 )

• O(n)

• 時間複雜度

• n 個狀態,每個狀態轉移 O(n)

• O(n2)

(6)

LIS 的優化 LIS 的優化

• 已知 LIS 有 O(n2) 的演算法,是否能做到更快呢?

• f(n)=max(f(i))+1, for all i<n and arr[i]<arr[n]

• 觀察到,對於任一個位置 i ,若存在 j 使得

arr[j]≦arr[i]

d p[j]≧dp[i]

,則 dp[i] 永遠不會被用到

arr 1 3 2 5 4

dp 1 2 2 3

(7)

LIS 優化 LIS 優化

• 因此,我們可以另外開一個陣列 (tmp) ,記錄「有可能被取到的 arr[i] 及 dp[i] 」

• 又,因為 LIS 長度每次只會加 1 ,因此可以再簡化成只記錄 arr 的值即可。此時,

tmp[i]=min(arr[j]), where dp[j]=i

arr 1 3 2 5 4

dp 1 2 2 3

tmp

(arr,dp) (1,1) (2,2) (5,3)

(8)

LIS 優化 LIS 優化

• 因此,我們可以另外開一個陣列 (tmp) ,記錄「有可能被取到的 arr[i] 及 dp[i] 」

• 又,因為 LIS 長度每次只會加 1 ,因此可以再簡化成只記錄 arr 的值即可。此時,

tmp[i]=min(arr[j]), where dp[j]=i

• 我們可以發現, tmp 內的元素是嚴格遞增的!

arr 1 3 2 5 4

dp 1 2 2 3

tmp 1 2 5

(9)

LIS 優化 LIS 優化

• 於是,狀態轉移式可以變成:

• f(n)=i+1,

where tmp[i] < arr[n] and tmp[i+1] ≧ arr[n]

• 由於 tmp 為嚴格遞增,可以用二分搜在 log(n) 時間內得到 i 值

,也就是說,此狀態轉移為 O(log(n))

• 於時我們得到了一個 O(n*log(n)) 的 LIS 演算法!

(10)

LCS LCS

• Longest Common Subsequence ( 最長共同子序列 )

• ex: s1[] = “abcabc”, s2[] = “abbcad”, 則 LCS 為 “ ab ca”, “abbc”,... 等,長度為 4

• 本題只需要求出 LCS 的長度即可

(11)

LCS LCS

• 定義狀態

• f(n,m) 表示 s1[0~n] 和 s2[0~m] 的 LCS 長度

• 最後答案為 f(N-1,M-1)

• 對於 f(n,m) ,考慮 s1[n] 和 s2[m] ,此時分成幾種 case

• s1[n] 和 s2[m] 都是 LCS 的一部分,則因為這兩個字元都是字串的最後一 個字元, s1[n]=s2[m] ,剩下的 LCS 則在 s1[0~n-1] 和 s2[0~m-1] 。因 此, f(n,m) = f(n-1,m-1) + 1

• 只有 s1[n] 是 LCS 的一部分,則表示 s2[m] 沒有貢獻, f(n,m)=f(n,m-1)

• 只有 s2[m] 是 LCS 的一部分,則 f(n,m)=f(n-1,m)

• s1[n] 和 s2[m] 都不是 LCS 的一部分,則 f(n,m)=f(n-1,m-1)

(12)

LCS LCS

• 定義狀態

• f(n,m) 表示 s1[0~n] 和 s2[0~m] 的 LCS 長度

• 最後答案為 f(N-1,M-1)

• 狀態轉移

• f(n,m)=max(f(n-1,m-1)+1,f(n-1,m),f(n,m-1),f(n-1,m-1)), if s1 [n]=s2[m]

• f(n,m)=f(n-1,m-1)+1, if s1[n]=s2[m]

• f(n,m)=max(f(n-1,m),f(n,m-1),f(n-1,m-1)), if s1[n]!=s2[m]

• f(n,m)=max(f(n-1,m),f(n,m-1)), if s1[n]!=s2[m]

(13)

LCS LCS

• 定義狀態

• f(n,m) 表示 s1[0~n] 和 s2[0~m] 的 LCS 長度

• 最後答案為 f(N-1,M-1)

• 狀態轉移

• f(n,m)=f(n-1,m-1)+1, if s1[n]=s2[m]

• f(n,m)=max(f(n-1,m),f(n,m-1)), if s1[n]!=s2[m]

• O(1) 轉移

• 時間複雜度

• 狀態 O(n2)* 轉移 O(1)=O(n2)

(14)

LCS LCS

• 用 LIS 做 LCS

• ex: s1[] = “abc”, s2[] = “bac”

• 記錄每個字元在 s1 出現的位置

• a: 0

• b: 1

• c: 2

• 接著將 s2 的字元換成上一步中對應的數字

• “bac” => {1,0,2}

• 然後對這個序列做 LIS ,結果就是原本兩個字串的 LCS

• LIS = {1,2} or {0,2}

• 對應到 “ bc” or “ac”

(15)

LCS LCS

• 用 LIS 做 LCS

• 如果一個字元在 s1 有重複出現呢?

• ex: s1[] = “abcabc”, s2[] = “abbcad”

• 記錄出現位置

• a: 0,3

• b: 1,4

• c: 2,5

• 由大到小排列

• 避免相同的字元被算兩次

• {3,0,4,1,4,1,5,2,3,0}

• LIS: {0,1,2,3} ,長度為 4

(16)

空間優化 空間優化

• 給一個 N*M 的矩形,每格內有一個數字。由左上走到右下,且只 能往右走和往下走的路徑中,總和最大為多少?

• 定義狀態

• f(i,j) 為走到點 (i,j) 時,路徑的最大值

• 狀態轉移

• f(i,j)=max(f(i-1,j),f(i,j-1))+a[i][j]

• 最後答案

• f(N,M)

(17)

空間優化 空間優化

• 如何儲存狀態?

• 開一個 N*M 的二維陣列

• 能不能開少一點?

• 注意到 f(n,?) 只會用到 f(n-1,?) 和 f(n,?)

• 不會用到 f(n-2,?) 、 f(n-3,?) 等狀態!

• 滾動數組

• 壓成 1 維陣列

(18)

空間優化 空間優化

• 只開兩個陣列,做完一次之後將結果複製到原本的陣列

i=n-1

i=n i=n

i=n+1

(19)

空間優化 空間優化

• 滾動數組:兩個陣列交替使用

• dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j]

• dp[i%2][j]=max(dp[(i+1)%2][j],dp[i%2][j])+a[i][j]

i=n i=n-1 i=n+1 i=n+2

(20)

空間優化 空間優化

• 只使用 1 維陣列

• 注意到,每個狀態 (

紅色

) 只會使用到上面一格和左邊一格的狀 態 (

藍色

)

• 開一個一維陣列表示右圖中綠色格子的值: dp[j]

(21)

空間優化 空間優化

• dp[j]=max(dp[j-1],dp[j])+a[i][j]

(22)

空間優化 空間優化

• 當轉移式為 f(i,j)=min(f(i,j+1),f(i-1,j))+a[i][j]

• dp[j]=min(dp[j+1],dp[j])+a[i][j]

• j 由 M 掃到 1

(23)

空間優化 空間優化

• 當轉移式為 f(i,j)=min(f(i-1,j),f(i-1,j-1))+(i+j)

• dp[j]=min(dp[j],dp[j-1])+(i+j)

• j 由 M 掃到 1

(24)

0/1 背包問題 0/1 背包問題

• 有一個可以耐重 W 的背包,及 N 個物品,每個物品有各自的重量 wi 和價值 vi ,求在不超過重量限制的情況下往背包塞盡量多的東 西,總價值最大為多少?

• 如果 wi 和 vi 都很大,則此問題為一 NP 問題,但如果範圍較小,

則可以用 DP 的方法解決

• 暴力法

:窮舉 2N 種可能的取法,找重量小於 W 的 vi 總合最大值

(25)

0/1 背包問題 0/1 背包問題

• 定義狀態

• f(n,m) 表示從前 n 個物品中選出重量總和恰為 m 的物品時,價值總合的 最大值。若不存在一種取法使得重量為 m ,則 f(n,m)=-INF( 或是其他數 值,如 -1)

• 狀態數: N*W ( 重量超過 W 就不需要考慮了 )

• 狀態轉移

• 從前 n 樣物品中選擇物品的最佳方案,一定是「有選到第 n 樣物品」和

「沒選到第 n 樣物品」其中一個 ( 或者兩者一樣好 )

• 如果最佳方案包含第 n 樣物品,則此最佳方案必為「選擇第 n 樣物品」

及「從前 n-1 項物品中取出重量為 m-wn 的最佳方案」,因此可以得到 f (n,m) = f(n-1,m-wn)+vn

• 如果最佳方案不包含第 n 樣物品,則 f(n,m) = f(n-1,m)

(26)

0/1 背包問題 0/1 背包問題

• 狀態轉移

• f(n,m) = max(f(n-1,m), f(n-1,m-wn)+vn), m ≧ wn

• f(n,m) = f(n-1,m), m < wn

• 初始條件

• f(0,0) = 0

• f(0,k) = -INF, for k>0

• 最後答案

• max(f(N,k)), 0 ≦ k ≦ W

(27)

0/1 背包問題 0/1 背包問題

• 實做

• 注意到, f(n,m) 只依賴於 f(n-1,m) 及 f(n-1,m-wn)

• 滾動數組

• dp[n%2][m]=max(dp[(n+1)%2][m], dp[(n+1)%2][m-w[n]]+v[n])

• 開一維陣列

• m 從 M 跑到 0

• dp[m]=max(dp[m], dp[m-w[n]]+v[n])

• 時間複雜度

• O(NW) , N 為物品個數, W 為背包重量上限

(28)

0/1 背包問題 0/1 背包問題

• 另解

• 定義狀態

• f(n,m) 表示從前 n 個物品中選出價值總和恰為 m 的物品時,重量總合的 最小值。若不存在一種取法使得價值為 m ,則 f(n,m)=INF

• 狀態數: N*V (V 為所有物品總合 )

• 狀態轉移

• 如果最佳方案包含第 n 樣物品,則此最佳方案必為「選擇第 n 樣物品」

及「從前 n-1 項物品中取出價值為 m-vn 的最佳方案」,因此可以得到 f (n,m) = f(n-1,m-vn)+wn

• 如果最佳方案不包含第 n 樣物品,則 f(n,m) = f(n-1,m)

(29)

0/1 背包問題 0/1 背包問題

• 狀態轉移

• f(n,m) = min(f(n-1,m), f(n-1,m-vn)+wn), m ≧ vn

• f(n,m) = f(n-1,m), m < vn

• 初始條件

• f(0,0) = 0

• f(0,k) = INF, for k>0

• 最後答案

• max(k), for all 0 ≦ f(N,k) ≦ W

• 時間複雜度

• O(NV) , N 為物品個數, V 為物品價值總合

(30)

0/1 背包問題 0/1 背包問題

• 比較兩種做法

• 用重量做為狀態

• 空間複雜度: O(W)

• 時間複雜度: O(NW)

• 限制: W 不能太大

• 用價值做為狀態

• 空間複雜度: O(V)

• 時間複雜度 : O(NV)

• 限制: V 不能太大

(31)

無限背包問題 無限背包問題

• 有一個可以耐重 W 的背包,及 N 種物品,每種物品有各自的重量 wi 和價值 vi ,且數量為無限多,求在不超過重量限制的情況下往 背包塞盡量多的東西,總價值最大為多少?

• 定義狀態

• f(n,m) 表示從前 n 種物品中選出重量總和恰為 m 的物品時,價值總合的 最大值。若不存在一種取法使得重量為 m ,則 f(n,m)=-INF

(32)

無限背包問題 無限背包問題

• 狀態轉移

• 從前 n 樣物品中選擇物品的最佳方案,一定是「第 n 樣物品取了 0 個」

、 「第 n 樣物品取了 1 個」 ... 「第 n 樣物品取了 k 個」中的最佳方案

,其中 k 為滿足 wi*k ≦ m 的最大可能值

• f(n,m)=max(f(n-1,m),

        f(n-1,m-wn)+vn,                   f(n-1,m-2*wn)+2*vn,

        ...,

        f(n-1,m-k*wn)+k*vn   )

• 整理之後得到 f(n,m)=max(f(n-1,m), f(n,m-wn)+vn)

(33)

無限背包問題 無限背包問題

• 實做

• 注意到, f(n,m) 只依賴於 f(n-1,m) 及 f(n,m-wn)

• 滾動數組

• dp[n%2][m]=max(dp[(n+1)%2][m], dp[n%2][m-w[n]]+v[n])

• 開一維陣列

• m 從 0 跑到 M

• dp[m]=max(dp[m], dp[m-w[n]]+v[n])

• 時間複雜度

• O(NW) , N 為物品個數, W 為背包重量上限

(34)

有限背包問題 有限背包問題

• 有一個可以耐重 W 的背包,及 N 種物品,每種物品有各自的重量 wi 和價值 vi ,且數量為 ki

個,求在不超過重量限制的情況下往

背包塞盡量多的東西,總價值最大為多少?

• 做法:將 ki 個相同物品視為不同物品,做 0/1 背包,時間複雜度 為 O(NWK) ,其中 K 為重複數量的最大值

(35)

有限背包問題 有限背包問題

• 另一種做法

• 狀態轉移

• f(n,m)=max(f(n-1,m), f(n-1,m-wn)+vn, f(n-1,m- 2*wn)+2*vn,...,f(n-1,m-k*wn)+k*vn), where k ≦ ki

• O(K) 轉移

• 時間複雜度還是 O(NWK)

• 更快的做法將在之後的課程中提到,有限背包問題有時間複雜度 O(NW*log K) 及 O(NW) 的做法

(36)

換零錢問題 換零錢問題

• 有 N 個不同的銅板,面額分別為 c[1~N] ,問

• 1. 能不能湊出恰好 M 元?

• 2. 如果可以,共有幾種方法?

• 3. 如果可以,最少需要用幾個銅板?

• 4. 能不能分成兩堆,使得兩堆的總和相等?

• 5. 能不能分成兩堆,使得兩堆的總和為 x:y ?

• 6. ...

(37)

換零錢問題 換零錢問題

• 其實這根本是弱化版的背包問題!

• 可以想成每樣物品有重量 c[i] ,價值為 1

• 1. 能不能湊出恰好 M 元?

• 因為只問「能不能湊出」,因此只需要開 bool 陣列就可以了

• f(n,m) = f(n-1,m) OR f(n-1,m-c[n])

• 2. 湊出 M 元的方法數有幾種?

• f(n,m) = f(n-1,m) + f(n-1,m-c[n])

(38)

換零錢問題 換零錢問題

• 3. 湊出 M 元需要最少的銅板數是幾個?

• 完全是背包問題,只是最大值改成最小值

• f(n,m) = min(f(n-1,m), f(n-1,m-c[n])+1)

• 4. 能不能分成兩堆,使得兩堆總和相等?

• 只要看能不能湊出 sum(c[i])/2 元就可以了

• 5. 能不能分成兩堆,使得兩堆總和為 x:y ?

• 只要看能不能湊出 sum(c[i])*x/(x+y) 元就可以了

參考文獻

相關文件

• 我們通常用 nD/mD 來表示一個狀態 O(N^n) ,轉移 O(N^m) 的 dp 演算法. • 在做每題

• 也就是 ”我的dp是n^3”這句話本身不夠表示你的dp演算法,必須 要說“我的dp是個狀態n^2,轉移n”才夠精確. •

[r]

• 有一個可以耐重 W 的背包,及 N 種物品,每種物品有各自的重量 w[i] 和價值 v[i] ,且數量為 k[i] 個,求在不超過重量限制的情 況下往背包塞盡量多的東西,總價值最大為多少?.

依賴背包問題 and

creted by howard41436 edited by

[r]

[r]