《黑色沙漠》韓服新寵物羊駝預告 超可愛萌萌噠
01揹包
有一個容量為\(V\)的揹包和\(N\)種物品。知道每種物品的價值\(w_{i}\)和體積\(v_{i}\)(一種物品只有一個),求在揹包能裝下的情況下能拿走的物品的最大總價值。
思路:
\(f_{ij}\)表示前\(i\)種中能拿走的物品總體積為\(j\)時能拿走的物品的最大總價值。對於第\(i\)種物品,有選或不選兩種情況,選了,前\(i-1\)種物品中能拿走的物品總體積為\(j-v_{i}\)就是\(f_{ij}\)的前一個狀態,即\(f_{ij}=f_{i-1j-v_{i}}+w_{i}\),不選,就是從\(f_{i-1j}\)直接轉移過來,即\(f_{ij}=f_{i-1j}\)
程式碼:
#include<iostream> using namespace std; int N,V; int v[110],w[110]; int f[110][1010]; int ans=0; int main(){ cin>>V>>N; for(int i=1;i<=N;i++){ cin>>v[i]>>w[i]; } for(int i=1;i<=N;i++){ for(int j=0;j<=V;j++){ if(j>=v[i])f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]); else f[i][j]=f[i-1][j]; ans=max(ans,f[i][j]); } } cout<<ans; return 0; }
完全揹包
這個和01揹包只有一點不同,每種物品有無窮多個。
思路:
這裡每種物品可以不拿,拿一個,拿兩個,……只要揹包能裝下,即\(f_{ij}= \underset{0<=k<=j/v_{i}}{max} f_{i-1j-k*v_{i}}+k*w{i}\)
程式碼:
#include<iostream> using namespace std; int N,V; int v[110],w[110]; int f[110][1010]; int ans=0; int main(){ cin>>N>>V; for(int i=1;i<=N;i++){ cin>>v[i]>>w[i]; } for(int i=1;i<=N;i++){ for(int j=0;j<=V;j++){ for(int k=0;k<=j/v[i];k++){ f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]); } ans=max(ans,f[i][j]); } } cout<<ans; return 0; }
但是這樣的時間複雜度是\(O(n^{3})\),很慢,既然每件物品能拿無窮多個,也就是說無論什麼情況下(揹包不能裝了除外),可以拿任何一種物品,\(f_{j}\)表示用\(j\)的空間能拿走的物品價值總和最大值,即\(f_{j}=max(f_{j},f_{j-v_{i}}+w_{i})\)
程式碼:
#include<iostream>
using namespace std;
int N,V;
int v[1010],w[1010];
int f[1010];
int ans=0;
int main(){
cin>>N>>V;
for(int i=1;i<=N;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=N;i++){
for(int j=0;j<=V;j++){
if(j>=v[i])f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
for(int j=0;j<=V;j++){
ans=max(ans,f[j]);
}
cout<<ans;
return 0;
}
多重揹包
也是隻與01揹包有一處不同,就是每種物品有\(s_{i}\)個。
思路:
只需要在完全揹包的\(O(n^{3})\)做法上加一個限制\(k<=s[i]\),即\(f_{ij}= \underset{0<=k<=min(j/v_{i},s[i])}{max} f_{i-1j-k*v_{i}}+k*w{i}\)
程式碼
#include<iostream>
using namespace std;
int n,v;
int u[110],w[110],s[110];
int f[110][110];
int ans=0;
int main(){
cin>>n>>v;
for(int i=1;i<=n;i++){
cin>>u[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){
for(int j=v;j>=0;j--){
for(int k=0;k<=min(s[i],j/u[i]);k++){
f[i][j]=max(f[i][j],f[i-1][j-k*u[i]]+k*w[i]);
}
ans=max(ans,f[i][j]);
}
}
cout<<ans;
return 0;
}
但是\(O(n^{3})\)還是慢,這裡可以用二進位制優化,是將每種物品進行分組,第一組1個,第二組2個,第三組4個,以此類推,直到不滿一組,則剩下的全部歸為一組。
這樣可以組合成所有情況,從第一組開始,可以滿足\([0,1]\)中的所有情況,第二組滿足\([2,3]\)中的所有情況,加上前面就是可以滿足\([0,3]\)中所有情況,以此類推,到最後一組就可以滿足\([0,s_{i}]\)中的所有情況。每組只有一個,這就是01揹包問題。
程式碼:
#include<iostream>
using namespace std;
int n,v;
int u[1010],w[1010],s[1010];
int wet[12010],val[12010],t[12010];
int f[2010];
int cnt=0;
int main(){
cin>>n>>v;
for(int i=1;i<=n;i++){
cin>>u[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){
int k=1;
while(k<s[i]){
t[++cnt]=k;
s[i]-=k;
wet[cnt]=t[cnt]*u[i];
val[cnt]=t[cnt]*w[i];
k*=2;
}
t[++cnt]=s[i];
wet[cnt]=t[cnt]*u[i];
val[cnt]=t[cnt]*w[i];
}
for(int i=1;i<=cnt;i++){
for(int j=v;j>=0;j--){
if(j>=wet[i])f[j]=max(f[j],f[j-wet[i]]+val[i]);
}
}
int ans=0;
for(int i=0;i<=v;i++){
ans=max(ans,f[i]);
}
cout<<ans;
return 0;
}
分組揹包
就是在01揹包的基礎上,將物品進行了分組,且規定一組中只能選擇一件物品。
思路:
也是在01揹包上進行輕微改動,\(f_{ij}\)中的\(i\)表示前\(i\)組,其餘照01揹包做就可以了。
程式碼:
#include<iostream>
using namespace std;
int n,v;
int mo[110],cnt;
int u[10010],w[10010];
int f[110][110];
int ans=0;
int main(){
cin>>n>>v;
cnt=1;
for(int i=1;i<=n;i++){
cin>>mo[i];
mo[i]=mo[i-1]+mo[i];
for(;cnt<=mo[i];cnt++){
cin>>u[cnt]>>w[cnt];
}
}
cnt=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=v;j++){
f[i][j]=f[i-1][j];
}
for(;cnt<=mo[i];cnt++){
for(int j=0;j<=v;j++){
if(j>=u[cnt])f[i][j]=max(f[i][j],f[i-1][j-u[cnt]]+w[cnt]);
}
}
for(int j=0;j<=v;j++){
ans=max(ans,f[i][j]);
}
}
cout<<ans;
return 0;
}