【學習筆記】——揹包 之01揹包
阿新 • • 發佈:2022-05-20
01揹包:
01揹包之所以叫做01揹包,是因為對於每件物品,都只有兩種情況:要或不要
例題:01揹包
分析:每種物品只有兩種情況:要或不要,典型的01揹包
狀態:f[i][v]:前 i 件物品放在 v 空間內產生的最大價值
推導方程:
當第 i 件物品放時:f[i][j]=f[i-1][j-w[i]]+c[i] (即:前 i-1 件物品放在 (j-第 i 件物品所佔空間)時的最大價值 + 第 i 件物品的價值)
不放時:f[i]=f[i-1][j](即:前 i-1 件物品放在 j 空間中的最大值)
又因為要取最大價值,所以:f[i][j]=max(f[i-1][j-w[i]]+c[i],f[i-1][j])
#include<bits/stdc++.h> using namespace std; int n,m; int f[1000][1000]; int w[100100],c[100100]; int main() { cin>>m>>n; for(int i=1;i<=n;i++) cin>>w[i]>>c[i]; for(int i=1;i<=n;i++) for(int j=n;j>=1;j--) f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]); cout<<f[n][m]; return 0; }
但這樣的時間、空間複雜度均為O(n*v),其中時間複雜度已無法優化,但空間還可以優化
優化方法一:滾動陣列
如果仔細看一下方程,不難發現,對於當前的f[i][j],其實只用到了第 i 行和第 i-1 行
所以,我們可以將f[n][m]定義為f[2][m](n,m均為長量),再定義兩個變數:pre、cur( pre 表示該行的上一行,cur 表示該行)。
但我們怎麼更改 pre 與 cur 的值呢?
非常簡單:swap(cur,pre) 即可;
#include<bits/stdc++.h> using namespace std; int n,m,cur=1,pre; int w[100100],c[100100],f[1000][1000]; int main() { cin>>n>>m; for(int i=1;i<=m;i++) cin>>w[i]>>c[i]; for(int i=1;i<=m;i++) { swap(cur,pre); for(int j=n;j>=1;j--) { if(j>=w[i]) f[cur][j]=max(f[pre][j],f[pre][j-w[i]]+c[i]); else f[cur][j]=f[pre][j]; } } cout<<f[cur][n]; return 0; }
優化方法二:
優化原理同上,不過比上一個更狠:直接壓成一維
方程:f[j]=max(f[j],f[j-w[i]]+c[i])
解釋:
在更新f[j]之前,f[j]相當於之前的f[i-1] [j](可以理解為:a=a+3;) 而更新後,f[j]就成了f[i] [j]
所以,必須從後往前更新,否則,方程就成了f[i][j]=max(f[i][j-w[i]]+c[i],f[i-1][v])
程式碼:
#include<bits/stdc++.h>
using namesapce std;
int n,m;
int w[100100],c[100100];
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++)
cin>>w[i]>>c[i];
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
if(j>=w[i])
f[j]=max(f[j],f[j-w[i]]+c[i]);
else
f[j]=f[j-1];
cout<<f[n][m];
return 0;
}