01背包问题dp优化
背包容量m給定,選擇n件物品,求放入背包的最大價值。
其中,物品只能選擇一次,要么放,要么不放。
樸素解法
經典的DP問題
狀態:f[i][j]f[i][j]f[i][j]表示選擇前iii個物品,背包容量為jjj時的最大價值
狀態轉移:
分為兩種情況
綜上兩種情況,狀態轉移方程是
f[i][j]=max(f[i?1][j],w[i]+f[i?1][j?v[i]])f[i][j]=max(f[i-1][j],w[i]+f[i-1][j-v[i]])f[i][j]=max(f[i?1][j],w[i]+f[i?1][j?v[i]])
ac代碼
#include<iostream> #include<cstring> using namespace std;const int maxn=1e3+10; int n,m; int v[maxn],w[maxn]; int dp[maxn][maxn];int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>v[i]>>w[i];}memset(dp,0,sizeof(dp));//全部置零 for(int i=1;i<=n;i++){//枚舉物品個數 for(int j=1;j<=m;j++){//枚舉背包剩余容量 dp[i][j]=dp[i-1][j];if(j>=v[i])dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);}} cout<<dp[n][m]<<endl;}空間優化后解法
空間優化后的ac代碼
空間優化需要在原來樸素解法的基礎上進行等價變形
樸素解法是
現在只需要一維數組dp[maxn],因為本層的結果只與上層的結果有關。
等價變形的時候去掉第一維,仍然需要和上面兩維邏輯上結果一樣。
下面來看一下去掉一維之后的結果
第一句dp[j]=dp[j];//相當于dp[i][j]=dp[i-1][j],因為先計算右邊的dp[j],對于第i層而言,這個dp是i-1層的計算結果,也就是滿足dp[i][j]=dp[i-1][j],這個等價可以。
第二句dp[j]=max(dp[j],dp[j-v[i]]+w[i]);等價于dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);嗎?很遺憾,不等價。為什么?你想啊,等式右邊的dp[j-v[i]]小于等式左邊的dp[j],也就是說,對于第i層而言,dp[j]使用的是本層的dp[j-v[i]],因為從小到大遍歷,當然本層中位于前面的提前計算過。也就是說dp[j]=max(dp[j],dp[j-v[i]]+w[i]);等價于第i層dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
而不是第(i-1)層。
怎么樣才能等價于第i-1層呢?
可以從大到小遍歷。因為在計算dp[j]的時候,本層的dp[j-v[i]]還沒有算過
,此時dp[j-v[i]]使用的是上層的結果。即等價于dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
這里遍歷背包容量的時候,需要從大到小遍歷for(int j=m;j>=v[i];j--)
for(int i=1;i<=n;i++)//遍歷物品for(int j=m;j>=v[i];j--){//遍歷背包容量dp[j]=max(dp[j],dp[j-v[i]]+w[i]);}以后寫題,按照這個板子寫。
#include<iostream> #include<cstring> using namespace std;const int maxn=1e3+10; int n,m; int v[maxn],w[maxn]; int dp[maxn];int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>v[i]>>w[i];}memset(dp,0,sizeof(dp));//全部置零 for(int i=1;i<=n;i++)for(int j=m;j>=v[i];j--){dp[j]=max(dp[j],dp[j-v[i]]+w[i]);}cout<<dp[m]<<endl;return 0; }總結
以上是生活随笔為你收集整理的01背包问题dp优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【级数】【马尔科夫链】n乘以x的n次方的
- 下一篇: 账户余额和可用余额为什么不一样