0-1揹包
有n件物品和乙個容量為v的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。重要的點在於:每種物品僅有一件,可以選擇放/不放子問題:f[i][v]表示前i件物品恰好放入乙個 容量為v 的揹包可以獲得的最大價值。
狀態轉移方程(遞推式):f[i][v]=max;//考慮前i件物品放入這個子問題的時候,可以轉化為前i-1件物品已經放好。那麼如果放入第i件物品,那麼問題轉化為 前i-1件物品放入剩餘容量為v-c[i]的揹包裡;如果不放入第i件物品,那麼問題轉化為 前i-1件物品放入剩餘容量為v的揹包裡。
而如果放入第i件物品,那麼當前價值就是f[i-1][v-c[i]]+w[i]。因此當前最大價值就是 放入&不放入 之間的最大值。
可以反向找到各種物品的選擇:從dp[n][v]開始,如果dp[i][j]=dp[i-1][j],則當前第i件物品沒有被選中,從dp[i-1][j]繼續找;否則,則表示選中,從dp[i-1][j-w[i]]開始找
偽**:
int dp=new如何優化呢?那麼只能在空間複雜度上進行優化,只使用一維陣列來存放結果int[n][v+1
];//
初始化第一行
//僅考慮容量為v的揹包放第0個物品,不放物品,價值為0
for(int i=0;i<=v;i++)
//初始化第一列
//容量為0的揹包放物品,不放物品,價值為0
for(int i=0;i<=n;i++)
//根據狀態轉移方程,填充其他行和列
for(int i=1;i)
//容量足夠,可以放進去,比價值更大的方法
else
}}//
最後的結果
dp[n-1][v]
此時狀態轉移方程為:f[v]=max;//那麼這個時候需要注意的是,在第二層迴圈中,需要使用從後往前計算,得到結果。即要用一維陣列記憶化的時候,需要用到當前位置的值 和 該位置之前的值。因為如果我們需要計算f[4, 4]=max、f[4,3]=max、f[4,2]=max。
如果是轉化為一維陣列,因為需要保證max中的f[v]是f[i-1][v],前面的f[v]是f[i][v]。也就是當前這一層的d[j]還沒有被更新過,所以當前的d[j]用到的是i-1層的結果。如果從前往後計算,那麼下一次使用的d[j]是本層已經更新過的,會覆蓋掉i-1層的結果。
//解釋
對於dp[j]=max而言,dp[j-w[i]]相當於二維的dp[i-1][j-w[i]],dp[j]是由前面的dp(1...j)推出來的。
因此比如從i=3推i=4,此時一維陣列存放,這是i=3時所有子問題的解。如果從前往後推,那麼計算i=4時,
dp[0]=0, dp[1]=0, ... , (前面這幾項都放不進 w[i]=5的物品)dp[5]=max=7, dp[6]=max=7, dp[7]=max=9.....這裡會更新dp[5]、dp[6]...的值,那麼後續計算的時候 就沒辦法用到 上一輪迴圈時的 dp[5]、dp[6]....了(即 因為當前值 是由上一輪迴圈推出來的,如果從前往後,前一次迴圈儲存下來的值 可能會被修改)就是我當前更新要用到這個值,但是這個值 在從前往後更新時,已經被修改了,那麼我用到的就是錯誤的值了。
初始化的話:(初始化 實際上是 在沒有任何物品可以放入揹包時 的合法狀態)
如果問法是「恰好裝滿」的最優解,那麼除了dp[0]初始化為0,其他都應該設定為 負無窮大。這樣能保證最終的dp[v]為恰好裝滿揹包時的最優解。此時,只有容量為0的揹包 可以在 什麼都不裝且價值為0時被「恰好裝滿」,因為如dp[3]則表示,揹包容量為3時,恰好裝滿的價值,此時沒有合法的解,因此屬於未定義狀態,設為無窮大。
如果問法是「可以不裝滿」的最優解,那麼所有的都應初始化為0,因為「什麼都不裝」時,0就是合法解。
偽**:
int dp=new例題:給定乙個僅包含正整數的非空陣列,確定該陣列是否可以分成兩部分,要求兩部分的和相等int[v+1
];//
初始化第一行
//僅考慮容量為v的揹包放第0個物品,不放物品,價值為0
for(int i=0;i<=v;i++)
//根據狀態轉移方程,填充其他行和列
for(int i=1;i)}//
最後的結果
dp[v]
思路:即給定n個元素組成的陣列arr,陣列元素的和為sum。轉換成揹包問題,每個物品的重量和價值為arr[i],兩部分和相等,即揹包的限重為sum/2.
if(nums==null || nums.length==0例題擴充套件:傳入陣列的大小+乙個int型陣列,返回該陣列能否分成兩組, 使得兩組中各元素加起來的和相等。)int sum=0
;for(int
num : nums)
//如果sum不可以平分,那麼就不可分為兩塊
if(sum%2!=0
)sum/=2;//
定義boolean dp=new boolean[sum+1
];//
初始化dp[0]=true
;for(int i=1; i)}//
輸出dp[sum]
並且,所有5的倍數必須在其中乙個組中,所有3的倍數在另乙個組中(不包括5的倍數),能滿足以上條件,返回true;不滿足時返回false。
public完全揹包static
void
main(string args)
else
if (num%3==0
)
else
}//如果剛好都是3和5的倍數,那麼不需要進行接下來的步驟
if (othernums.size()==0
)
else
}//接下來問題轉化為,將剩餘不是3和5的倍數的數,均分到兩個組裡。使得
//3的倍陣列之和+組a=5的倍陣列之和+組b -->(3的倍陣列之和-5的倍陣列之和)+組a=組b-->可以把兩組之差當成乙個值
else
//如果不能均分,那麼返回false
if (sumtemp%2!=0
)
else
if (candivided(othernums, othernums.size(), sumtemp/2
))
else}}
}//是否可以將鍊錶中的數分為兩組,每組的和為總和的1/2。
//這裡用的是二進位制列舉,二進位制的每乙個01位可以當成這個數字加不加進去
private
static boolean candivided(listothernums, int size, int
sum)
else
}if (cursum ==sum)
}return
false
; }
//上乙個函式還可以用01揹包的方法來做。
private static boolean candivided(listothernums, int size, int sum)
}return dp[sum];
}
有n種物品和乙個容量為v的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。重要的區別在於完全揹包是 每種無限件
狀態轉移方程:f[i][j]=math.max(f[i-1][j-k*c[i]]+k*w[i]),0<=k*c[i]<=j;//根據第i件物品放多少件,即前i-1件物品中 選擇 若干件 放入剩餘的空間上,使得最大。(f[i][j]表示 前i種物品 放入乙個容量為j的揹包種獲得 最大價值)
//遞迴和動態規劃的區別:動態規劃多使用了乙個二維陣列來儲存中間的解
**:
int dp=new同樣使用一維陣列 來優化 空間複雜度int[n][v+1
];//
初始化第一行
//僅考慮容量為v的揹包放第0個物品,不放物品,價值為0
for(int i=0;i<=v;i++)
//初始化第一列
//容量為0的揹包放物品,不放物品,價值為0
for(int i=0;i<=n;i++)
//根據狀態轉移方程,填充其他行和列
for(int i=1;i)
//容量足夠,可以放進去,比價值更大的方法。取k個物品i,再k種選擇 選出 最優解
else
}
}}//
最後的結果
dp[n-1][v]
dp[i]=math.max(dp[i], dp[i-w[i]]+v[i])
int dp=new(示意圖源於網路,侵刪)int[v+1
];//
初始化第一行
//僅考慮容量為v的揹包放第0個物品,不放物品,價值為0
for(int i=0;i<=v;i++)
//根據狀態轉移方程,填充其他行和列
for(int i=1;i)}//
最後的結果
dp[v]
揹包問題 01揹包 完全揹包 多重揹包
01揹包和完全揹包的區別 01揹包的侷限在於每樣物品只有一種,每個物品都有乙個屬於自己的價值和重量,在給定的物品中選出揹包所能容納的最大重量,要求是價值最大 完全揹包與01揹包的不同在於完全揹包不限制每樣物品的個數,物品的價值和質量都與01揹包一樣,也同樣是求在給定大小的容量中,找出最大價值的選擇 ...
揹包問題(01揹包,完全揹包,多重揹包)
揹包問題 01揹包,完全揹包,多重揹包 近日為以下瑣事煩身 差不多要向學院提交專案申請了,本來是想做個多模式的im系統的,可是跟往屆通過審核的專案比起來,缺乏創新和研究價值,所以在文件上要多做手腳,花點心思。揹包問題,經典有揹包九講。不死族的巫妖王發工資拉,死亡騎士拿到一張n元的鈔票 記住,只有一張...
揹包問題 01揹包,完全揹包,多重揹包
有goods num件物品,max volume的最大裝載量,每種物品只有一件,每種物品都有對應的重量或者說體積volume i 價值value i 求解裝包的最大價值 假設目前已經有 i 1件物品裝在容量為 j 的揹包中,並且得到最大價值package i 1 j 當前裝第i件,那麼討論分兩個角度...