Dynamic Programming (2) 下
by music960633
空間優化 空間優化
• 給一個 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)
空間優化 空間優化
• 如何儲存狀態?
• 開一個 N*M 的二維陣列
• 能不能開少一點?
• 注意到 f(n,?) 只會用到 f(n-1,?) 和 f(n,?)
• 不會用到 f(n-2,?) 、 f(n-3,?) 等狀態!
• 滾動數組
• 壓成 1 維陣列
空間優化 空間優化
• 只開兩個陣列,做完一次之後將結果複製到原本的陣列
i=n-1
i=n i=n
i=n+1
空間優化 空間優化
• 滾動數組:兩個陣列交替使用
• 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
空間優化 空間優化
• 只使用 1 維陣列
• 注意到,每個狀態 ( 紅色 ) 只會使用到上面一格和左邊一格的狀 態 ( 藍色 )
• 開一個一維陣列表示右圖中綠色格子的值: dp[j]
空間優化 空間優化
• dp[j]=max(dp[j-1],dp[j])+a[i][j]
空間優化 空間優化
• 當轉移式為 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
空間優化 空間優化
• 當轉移式為 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
0/1 背包問題 0/1 背包問題
• 有一個可以耐重 W 的背包,及 N 個物品,每個物品有各自的重量 w
i和價值 v
i,求在不超過重量限制的情況下往背包塞盡量多的東 西,總價值最大為多少?
• 如果 w
i和 v
i都很大,則此問題為一 NP 問題,但如果範圍較小,
則可以用 DP 的方法解決
• 暴力法 :窮舉 2
N種可能的取法,找重量小於 W 的 v
i總合最大值
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)
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
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 為背包重量上限
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)
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 為物品價值總合
0/1 背包問題 0/1 背包問題
• 比較兩種做法
• 用重量做為狀態
• 空間複雜度: O(W)
• 時間複雜度: O(NW)
• 限制: W 不能太大
• 用價值做為狀態
• 空間複雜度: O(V)
• 時間複雜度 : O(NV)
• 限制: V 不能太大
無限背包問題 無限背包問題
• 有一個可以耐重 W 的背包,及 N 種物品,每種物品有各自的重量 w
i和價值 v
i,且數量為無限多,求在不超過重量限制的情況下往 背包塞盡量多的東西,總價值最大為多少?
• 定義狀態
• f(n,m) 表示從前 n 種物品中選出重量總和恰為 m 的物品時,價值總合的 最大值。若不存在一種取法使得重量為 m ,則 f(n,m)=-INF
無限背包問題 無限背包問題
• 狀態轉移
• 從前 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)
無限背包問題 無限背包問題
• 實做
• 注意到, 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 為背包重量上限
有限背包問題 有限背包問題
• 有一個可以耐重 W 的背包,及 N 種物品,每種物品有各自的重量 w
i和價值 v
i,且數量為 k
i個,求在不超過重量限制的情況下往 背包塞盡量多的東西,總價值最大為多少?
• 做法:將 k
i個相同物品視為不同物品,做 0/1 背包,時間複雜度
為 O(NWK) ,其中 K 為重複數量的最大值
有限背包問題 有限背包問題
• 另一種做法
• 狀態轉移
• 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) 的做法
換零錢問題 換零錢問題
• 有 N 個不同的銅板,面額分別為 c[1~N] ,問
• 1. 能不能湊出恰好 M 元?
• 2. 如果可以,共有幾種方法?
• 3. 如果可以,最少需要用幾個銅板?
• 4. 能不能分成兩堆,使得兩堆的總和相等?
• 5. 能不能分成兩堆,使得兩堆的總和為 x:y ?
• 6. ...
換零錢問題 換零錢問題
• 其實這根本是弱化版的背包問題!
• 可以想成每樣物品有重量 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])
換零錢問題 換零錢問題
• 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) 元就可以了