Dynamic Programming - 2
creted by howard41436
edited by lawfung
• 影片看了嗎
• Q&A
背包問題
• 今天要講各種背包問題
• 如果你在哪裡看過跟今天講課一樣的編排順序,那可能不是巧合
• c 是花費, w 是價值
背包問題
• 背包問題的原形有三種,讓我們一一來複習!
• 1. 0/1背包問題
• 2. 無限背包問題
• 3. 有限背包問題
1. 0/1背包問題
每個東西只有一個,取或不取
1. 0/1背包問題
f(n,m)=max(f(n-1,m),f(n-1,m-ci)+wi) 可以滾動陣列,或是甚至壓成一維陣列
for i = 1...n :
for j = m...0 :
f[j]=max(f[j],f[j-c[i]]+w[i])
2. 無限背包問題
每個東西有無限多個,愛取幾個就取幾個
2. 無限背包問題
f(n,m)=max(f(n-1,m),f(n,m-ci)+wi) 可以滾動陣列,或是甚至壓成一維陣列 for i = 1...n :
for j = 0...m :
f[j]=max(f[j],f[j-c[i]]+w[i])
2. 無限背包問題
這是正確的code嗎?
for j = 0...m : for i = 1...n :
f[j]=max(f[j],f[j-c[i]]+w[i])
3. 有限背包問題
每個東西有t[i]個,也就是可以不取或是取最多t[i]個
3. 有限背包問題
f(n,m)=max(f(n-1,m-k*ci)+k*wi) , for 0<=k<=t[i]
可以滾動陣列,或是甚至壓成一維陣列 for i = 1...n :
for j = m...0 :
for k = 0...t[i]:
f[j]=max(f[j],f[j-k*c[i]]+k*w[i]) 複雜度n*m*max{t[i]}
或是精確一點來說, m*sum(t[i])
物品拆分
我們可以做點變化:把第i個物品當做t[i]個不同的物品,但有一樣 的c[i]和w[i],然後做一般的背包問題。
這樣複雜度沒有變啊: sum{t[i]}*m = n*max{t[i]}*m 要組合出1~t[i]的數字,需要把t[i]拆成t[i]個1嗎?
用多少個數字可以組合出1~8所有數字呢?
是8個嗎?
物品拆分
將t[i]個物品拆成
1個,2個,4個,...,2^h個,t[i]-(2^{h+1}-1)個 其中h是滿足(2^{h+1}-1)<=t[i]的最大整數。
如此一來t[i]個物品就可以拆成lg(t[i])種不同物品就好!然後 新的背包就會有sum{lg(t[i])}個物品,每個物品選或不選,變回 0/1背包問題了!
複雜度O(n*lg(maxt)*m)
3. 有限背包問題
f(n,m)=max(f(n-1,m),f(n-1,m-ci)+wi) 可以滾動陣列,或是甚至壓成一維陣列
for i = 1...n’ : for j = m...0 :
f[j]=max(f[j],f[j-c[i]]+w[i]) 其實有複雜度更好的做法,下一次的影片會教到
4. 混合背包問題
每個東西有三種可能:
(1)只有一個 (2)有無限個 (3)有t[i]個 一樣問最大價值
4. 混合背包問題
一樣先對有限個數的做物品拆分(拆分完的就變只有一個) for i = 1...n’ :
if i 為只有一個的物品:
for j = m...0 :
f[j]=max(f[j],f(j-c[i])+w[i]) if i 為有無限個的物品:
for j = 0...m :
f[j]=max(f[j],f(j-c[i])+w[i])
5. 二維背包問題
每個東西有重量(d[i]),價錢(c[i]),跟價值(w[i]) 你除了要滿足sum(c[i])<=A,還要滿足sum(d[i])<=B 一樣問最大價值?
5. 二維背包問題
每個東西有重量(d[i]),價錢(c[i]),跟價值(w[i]) 你除了要滿足c[i]<=A,還要滿足d[i]<=B
一樣問最大價值?
開狀態f[n][A][B]即可,轉移為
f(n,A,B)=max(f(n-1,A,B),f(n-1,A-ci,B-di)+wi) 實作上一模一樣!
6. 分組背包問題
有t組物品,每個物品跟以往一樣有價錢,價值。
但是每組物品裡面最多只能選一個。
請問最大價值?
6. 分組背包問題
for i = 1...t :
for j = m...0 :
for k = 1...size(i):
f[j]=max(f[j],f[j-c[i][k]]+w[i][k]) 複雜度 sum(size(i))*m
6. 分組背包問題
這是正確的code嗎?
for i = 1...t :
for k = 1...size(i):
for j = m...0 :
f[j]=max(f[j],f[j-c[i][k]]+w[i][k])
7.背包合併
有兩堆物品A, B ,他們已經各自做好了背包DP儲存在h, k陣列,
你想要把兩堆物品合起來一起考慮,然後重做一個背包DP for i = 0...m :
for j = 0...i :
f[i]=max(h[j],k[i-j]) 複雜度O(m^2)
8. 背包問題的變化
(1)求最大價值的方法總數 (2)求最大價值的一組方案
(3)求最大價值的字典序最小的一組方案 (4)求次大價值的解/第K大價值的解
8. 背包問題的變化
(1)求最大價值的方法總數 for i = 1...n :
for j = m...0 :
if f[j]<f[j-c[i]]+w[i] : f[j]=f[j-c[i]]+w[i]
g[j]=g[j-c[i]]
else if f[j]==f[j-c[i]]+w[i] : g[j]+=g[j-c[i]]
8. 背包問題的變化
(2)求最大價值的一組方案 for i = 1...n :
for j = m...0 :
if f[i][j]<f[i-1][j-c[i]]+w[i] : f[i][j]=f[i-1][j-c[i]]+w[i]
g[i][j]=1 else:
g[i][j]=0
8. 背包問題的變化
(3)求最大價值的字典序最小一組方案 for i = 1...n :
for j = m...0 :
if f[i][j]<=(?)f[i-1][j-c[i]]+w[i] : f[i][j]=f[i-1][j-c[i]]+w[i]
g[i][j]=1 else:
g[i][j]=0 哪樣才是對的?
8. 背包問題的變化
(3)求最大價值的字典序最小一組方案 reverse(items)
for i = 1...n :
for j = m...0 :
if f[i][j]<=f[i-1][j-c[i]]+w[i] : f[i][j]=f[i-1][j-c[i]]+w[i]
g[i]=1 else:
g[i]=0
8. 背包問題的變化
(4)求第K大價值
//f[i][j] becomes a sorted vector for i = 1...n :
for j = m...0 :
for k = 0...K-1:
vec.push_back(f[i-1][m][k])
vec.push_back(f[i-1][m-c[i]][k]+w[i]) sort(vec)
f[i][j]=vec[0...K-1]
另類背包問題
by lawfung
其他背包?
● 分數背包
● V跟W都很大的背包
分數背包
• 每個物品都可以只取部分
分數背包
• 其實可以貪心(?
• 每次都直接取最性價比最高的。
V跟W都很大的背包
V跟W都很大的背包
V跟W都很大的背包
折半枚舉
• 今天把物品任意分兩堆A, B,
• 最後取的集合一定是
A的某子集,加上B的某個子集
• 假設我們已經知道A要取哪個子集了,B的子集該怎麼取?
• 令剩餘的背包大小(W - A子集重量總和)為L
• 那麼我們要取的是B的子集中,重量不大於L中價值最大的。
折半枚舉實作
• 先把B集合的子集枚舉出來成S
• 把S中的垃圾刪掉
(就是如果有兩種取法x, y,若是y取法的價值低於x,但是重量高 於x,那就把y刪掉)
• 最後枚舉A的子集,每次都在S二分搜重量,然後得到該種A子集取 法的最大價值
• 取所有最大價值的最大值
折半枚舉
練習題
• POJ 2758