背包问题
背包问题
背包,即询问一个背包装最大价值的物品总价值。
01 背包
例题:P1048 采药
想到使用 DP。
\[f_{i,j} =
\left\{\begin{matrix}
\max f_{i-1,j-c_i} , f_{i-1,j} & j \ge c_i\\
f_{i-1,j} & j < c_i
\end{matrix}\right.
\]
(公式中,\(f_{i,j}\) 表示使用前 \(i\) 个物品,重量小于 \(j\) 的价值最高量;\(c_i\) 指本物品的重量,\(w_i\) 指本物品的价值)
Code1
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int w[MAXN], c[MAXN];
int f[MAXN][MAXM];
int main()
{
int v,n;
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>c[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=v;j++)
{
if(j>=c[i]) f[i][j]=max(f[i-1][j-c[i]]+w[i],f[i-1][j]);
else f[i][j]=f[i-1][j];
}
}
cout<<f[n][v];
return 0;
}
空间复杂度:\(O(nm)\)
那有的题目就很难受,要求在线性空间内结束战斗,就需要通过滚动数组来实现。
Code2
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int w[MAXN], c[MAXN];
int f[2][MAXM];
int main()
{
int v,n;
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>c[i]>>w[i];
bool flag=true;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=v;j++)
{
if(j>=c[i]) f[flag][j]=max(f[1-flag][j-c[i]]+w[i],f[1-flag][j]);
else f[flag][j]=f[1-flag][j];
}
flag=1-flag;
}
cout<<f[1-flag][v];
return 0;
}
空间复杂度:\(O(2m)\)
当然,也可以使用一维数组。
Code3
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int w[MAXN], c[MAXN];
int f[MAXM];
int main()
{
int v,n;
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>c[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=v;j>=c[i];j--)
f[j]=max(dp[j],dp[j-c[i]]+w[i])
cout<<f[n][v];
return 0;
}
多重背包
多重背包的概念就是将 01 背包的物品数量变为有限的,而并非只有放和不放两种。
想到可以多次进行 01 背包即可。
Code1
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int dp[MAXN][MAXM];
int w[MAXN],c[MAXN],x[MAXN];
int main()
{
int n,v;
cin>>n>>v;
for(int i=1;i<=n;i++) cin>>w[i]>>c[i]>>x[i];
for(int i=1;i<=n;i++)
for(int j=0;j<=v;j++)
for(int k=0;k<=x[i];k++) //第三层!无法优化
if(j>=c[i]*k)
dp[i][j]=max(dp[i][j],dp[i-1][j-k*c[i]]+k*w[i]);
cout<<dp[n][v];
return 0;
}
时间复杂度:\(O(v \sum x_i)\)
Code2
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int w[MAXN], c[MAXN], m[MAXN];
int f[MAXM];
int main()
{
int n,v;
cin>>n>>v;
for(int i=1;i<=n;i++) cin>>c[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=v;j>=0;j--)
for(int k=0;k<=m[i];k++)
if(j>=c[i]*k)
dp[j]=max(dp[j],dp[j-c[i]*k]+w[i]*k);
cout<<f[v];
return 0;
}
多重背包二进制优化。
Code3
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int dp[MAXM];
int w[MAXN],c[MAXN];
int main()
{
int n,v;
cin>>n>>v;
int cnt=0;
for(int i=0;i<n;i++)
{
int a,b,s;
cin>>a>>b>>s;
for(int k=1;k<=s;k<<=1)
{
w[cnt]=k*a;
c[cnt]=k*b;
s-=k;
cnt++;
}
if(s!=0)
{
w[cnt]=s*a;
c[cnt]=s*b;
cnt++;
}
}
for(int i=0;i<cnt;i++)
for(int j=v;j>=c[i];j--)
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
cout<<dp[v];
return 0;
}
时间复杂度:\(O(v \sum \log x_i)\)!
完全背包
完全背包的概念就是将 01 背包的物品数量变为无限的,而并非只有放和不放两种。
想到肯定不可以多次进行 01 背包即可。
Code1
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int dp[MAXN][MAXM];
int w[MAXN],c[MAXN];
int main()
{
int n,v;
cin>>n>>v;
for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
for(int i=1;i<=n;i++)
for(int j=0;j<=v;j++)
if(j>=c[i]) dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i]);
cout<<dp[n][v];
return 0;
}
Code2
#include <iostream>
using namespace std;
#define MAXN 1005
#define MAXM 10005
int w[MAXN], c[MAXN];
int f[MAXM];
int main()
{
int n,v;
cin>>n>>v;
for(int i=1;i<=n;i++) cin>>c[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=c[i];j<=v;j++) //这里不一样!!!
f[j]=max(dp[j],dp[j-k*c[i]]+w[i]);
cout<<f[n][v];
return 0;
}

浙公网安备 33010602011771号