完全背包dp优化
01背包:每個物品只能選擇一次
狀態轉移方程
f[i][j]=max(f[i][j],f[i?1][j?v]+w)f[i][j]=max(f[i][j],f[i-1][j-v]+w)f[i][j]=max(f[i][j],f[i?1][j?v]+w)
完全背包:每個物品可以選擇無數次
狀態轉移方程
f[i][j]=max(f[i][j],f[i][j?v]+w)f[i][j]=max(f[i][j],f[i][j-v]+w)f[i][j]=max(f[i][j],f[i][j?v]+w)
其中v表示第i個物品的體積,w表示第i個物品的價值
狀態表示
集合:從前i個物品中選,體積不超過j
屬性:最大值max
?f[i][j]\Rightarrow f[i][j]?f[i][j]表示只從前i個物品中選,并且體積不超過j的方案的最大價值。
狀態計算
對于f[i][j]f[i][j]f[i][j],劃分集合,可以劃分為很多個
對于第i個物品,我們不選,對應的最大價值:f[i?1][j]f[i-1][j]f[i?1][j]
對于第i個物品,我們只選1個,對應的最大價值:f[i?1][j?v]+wf[i-1][j-v]+wf[i?1][j?v]+w
對于第i個物品,我們只選2個,對應的最大價值:f[i?1][j?2v]+2wf[i-1][j-2v]+2wf[i?1][j?2v]+2w
不失一般性,選擇k個,對應的最大價值:f[i?1][j?kv]+kwf[i-1][j-kv]+kwf[i?1][j?kv]+kw
只要不超過背包容量m的限制,可以一直選擇下去,假設最多放k件物品。
所以,狀態計算的方程為上述計算結果的最大值:
f[i][j]=max(f[i?1][j],f[i?1][j?v]+w,f[i?1][j?2v]+2w,...,f[i?1][j?kv]+kw,)(1)f[i][j]=max(f[i-1][j],f[i-1][j-v]+w,f[i-1][j-2v]+2w,...,f[i-1][j-kv]+kw,)(1)f[i][j]=max(f[i?1][j],f[i?1][j?v]+w,f[i?1][j?2v]+2w,...,f[i?1][j?kv]+kw,)(1)
使用變量代換:令j=j-v,此時f[i][j?v]f[i][j-v]f[i][j?v]表示只在前i個物品中選,背包容量不超過j-v的方案的價值最大值。
有
f[i][j?v]=max(f[i?1][j?v],f[i?1][j?2v]+w,f[i?1][j?3v]+2w,...,f[i?1][j?kv]+(k?1)w,)f[i][j-v]=max(f[i-1][j-v],f[i-1][j-2v]+w,f[i-1][j-3v]+2w,...,f[i-1][j-kv]+(k-1)w,)f[i][j?v]=max(f[i?1][j?v],f[i?1][j?2v]+w,f[i?1][j?3v]+2w,...,f[i?1][j?kv]+(k?1)w,)
這里并沒有增加一項f[i?1][j?(k+1)v]+kwf[i-1][j-(k+1)v]+kwf[i?1][j?(k+1)v]+kw,這是因為背包容量從j變成了j-v,如果背包容量為j時最多可以放k件物品,那么背包容量為j-v時,背包最多可以放k-1件物品。
我們發現
即(1)式的后半部分可以使用f[i][j-v]+w來表示
所以,最后的狀態轉移方程為
f[i][j]=max(f[i][j],f[i][j?v]+w)f[i][j]=max(f[i][j],f[i][j-v]+w)f[i][j]=max(f[i][j],f[i][j?v]+w)
ac代碼(樸素解法)
ac代碼(空間優化解法)
主要代碼
dp[j-v[i]]小于dp[j],此時從小到大遍歷背包容量時,dp[j-v[[i]]已經更新到本層,而不是上一層。滿足//相當于dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i])
根據閆氏dp分析法, 代碼更改之后需要邏輯沒變,發現等價后沒變。
這樣我們就省掉了空間。
#include<iostream> #include<cstring> using namespace std;const int maxn=1010;int n,m,dp[maxn],a[maxn],v[maxn],w[maxn]; int main(){cin>>n>>m;memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++){cin>>v[i]>>w[i];}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){//遍歷背包容量if(j>=v[i])dp[j]=max(dp[j],dp[j-v[i]]+w[i]);//背包容量為j時的最大價值 }cout<<dp[m]<<endl;//輸出背包容量為m時的結果 }更精簡的寫法
把遍歷背包容量直接提到循環里面for(int j=v[i];j<=m;j++)
以后寫完全背包就寫優化后的解法
總結
- 上一篇: 琨字取名的寓意
- 下一篇: 金辉梦瑶,这两个名字,我起个什么网名?