背包
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}\),两个式子合并后就是\(f_{ij}=max\left(f_{i-1j-v_{i}},f_{i-1j}\right)\)
代码:
#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;
}
浙公网安备 33010602011771号