0/1背包问题
1.状态函数
f
[
i
]
[
j
]
=
s
f[i][j]=s
f[i][j]=s:选完前面i个物品的时候,背包的体积是j,能够得到的最大的价值为s 。
2.答案
f
[
N
]
[
V
]
f[N][V]
f[N][V]
3.递推起点和边界
f
[
i
]
[
0
]
=
0
,
f
[
0
]
[
j
]
=
0
f[i][0]=0,f[0][j]=0
f[i][0]=0,f[0][j]=0
4、状态转移方程(递推关系)
for(int i=1;i<=N;i++)
for(int j=1;j<=V;j++)
if(j<v[i])
f[i][j]=f[i-1][j];
else
f[i][j]=max(f[i-1][j],w[i]+f[i-1][j-v[i]]);
法一:二维数组(从前往后推)
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[1000000],w[1000000];
int f[10000][10000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
}
for(int i=1;i<=N;i++)
{
for(int j=1;j<=V;j++)
{
if(j<v[i])
{
f[i][j]=f[i-1][j];
}
else
{
f[i][j]=max(f[i-1][j],w[i]+f[i-1][j-v[i]]);
}
}
}
cout<<f[N][V];
return 0;
}
法二:一维滚动数组(从前往后推)
f
[
j
]
f[j]
f[j] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|
| 0 | 0 | 1 | 3 | 5 | 5 | 6 | 8 | 9 | 10 | 12 |
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[100000],w[100000];
int f[100000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
}
for(int i=1;i<=N;i++)
{
for(int j=V;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]);
}
}
cout<<f[V];
return 0;
}
完全背包
法一:二维数组(从前往后推)
#include<bits/stdc++.h>
using namespace std;
int V,N;
int v[100000],w[100000];
int f[10000][10000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
}
for(int i=1;i<=N;i++)
{
for(int j=1;j<=V;j++)
{
if(j<v[i])
{
f[i][j]=f[i-1][j];
}
else
{
f[i][j]=max(f[i-1][j],w[i]+f[i][j-v[i]]);
}
}
}
cout<<"max="<<f[N][V];
return 0;
}
法二:一维滚动数组(从前往后推)
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[100000],w[100000];
int f[100000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
}
for(int i=1;i<=N;i++)
{
for(int j=v[i];j<=V;j++)
{
f[j]=max(f[j],w[i]+f[j-v[i]]);
}
}
cout<<"max="<<f[V];
return 0;
}
多重背包
法一:视为多次0/1背包(一维滚动数组从后往前推)
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[100000],w[100000],s[100000];
int f[100000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
cin>>s[i];
}
for(int i=1;i<=N;i++)
{
for(int k=1;k<=s[i];k++)
{
for(int j=V;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]);
}
}
}
cout<<f[V];
return 0;
}
法二:多件物品看成一个整体(一维滚动数组从后往前推)
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[100000],w[100000],s[100000];
int f[100000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
cin>>s[i];
}
for(int i=1;i<=N;i++)
{
for(int j=V;j>=0;j--)
{
for(int k=0;k<=s[i];k++)
{
if(k*v[i]>j)
{
break;
}
else
{
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<f[V];
return 0;
}
法三:二进制方法拆分为多件物品的组合(即成为了0/1背包)
拆分出来的每个大物品都是独一无二的(一维滚动数组从后往前推)
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[100000],w[100000],s[100000];
int f[100000];
int n;
int main()
{
cin>>N>>V;
int a,b,s;
for(int i=1;i<=N;i++)
{
cin>>a>>b>>s;
int k=1;
while(k<s)
{
n++;
v[n]=k*a;
w[n]=k*b;
s=s-k;
k=k*2;
}
n++;
v[n]=s*a;
w[n]=s*b;
}
for(int i=1;i<=N;i++)
{
for(int j=V;j>=v[i];j--)
{
f[j]=max(f[j],w[i]+f[j-v[i]]);
}
}
cout<<f[V];
return 0;
}
混合背包
一维滚动数组从后往前推
#include<bits/stdc++.h>
using namespace std;
int N,V;
int v[100000],w[100000],s[100000];
int f[100000];
int main()
{
cin>>V>>N;
for(int i=1;i<=N;i++)
{
cin>>v[i];
cin>>w[i];
cin>>s[i];
}
for(int i=1;i<=N;i++)
{
if(s[i]==0)
{
for(int j=v[i];j<=V;j++)
{
f[j]=max(f[j],w[i]+f[j-v[i]]);
}
}
else
{
for(int k=1;k<=s[i];k++)
{
for(int j=V;j>=v[i];j--)
{
f[j]=max(f[j],w[i]+f[j-v[i]]);
}
}
}
}
cout<<f[V];
return 0;
}
|