背包问题可以说是最经典的动态规划问题了。在这里,我们只讲解最基本的三种背包问题。
零一背包问题:有n种重量和价值分别为Wi和Vi的物品。从这些物品中挑选出总重量不超过w的物品,
每种物品都只能挑选一件,求所有挑选方案中价值总和的最大值。
完全背包问题:有n种重量和价值分别为Wi和Vi的物品。从这些物品中挑选出总重量不超过w的物品,
每种物品都可以挑选多件,求所有挑选方案中价值总和的最大值。
部分背包问题:有n种重量和价值分别为Wi和Vi的物品。从这些物品中挑选出总重量不超过w的物品,
第i种物品最多选mi个,求所有挑选方案中价值总和的最大值。
我们用dp[n][w]表示从前n种物品中挑选出总重量不超过w的物品时所用挑选方案价值总和的最大值。
初始化时dp[0][i]=dp[i][0]=0。大家可以自己画张表推导一下就明白了。
- 01背包
(开二维数组)
#include<iostream>
#include<algorithm>
using namespace std;
void solve();
int n,w;
int W[100]={0};
int V[100]={0};
int dp[100][100];
int main()
{
int i,j;
cin>>n>>w;
for(i=0;i<=n;i++) dp[i][0]=0;
for(j=0;j<=w;j++) dp[0][j]=0;
for(i=0;i<n;i++) cin>>W[i]>>V[i];
solve();
}
void solve()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<=w;j++)
{
if(j<W[i]) dp[i+1][j]=dp[i][j];
else dp[i+1][j]=max(dp[i][j],dp[i][j-W[i]]+V[i]);
}
}
cout<<dp[n][w]<<endl;
}
只涉及i与i+1可通过滚动数组实现
(开一维数组,防止数据过大而使二维数组无法存下)
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int a[50001],b[50001];
for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
int f[50001]={0};
for(int i=1;i<=n;i++){
for(int j=m;j>=a[i];j--)//开逆序为了避免子状态受到影响
f[j]=max(f[j-a[i]]+b[i],f[j]);
}
cout<<f[m]<<endl; //最优解
}
- 完全背包
//二维
for (int i = 1; i < n; i++){
for (int j = 1; j <= v; j++){
for (int k = 0; k*c[i] <= j; k++){
if(c[i]<=j) /*如果能放下*/
f[i][j] = max{f[i][j],f[i-1][j - k * c[i]] + k * w[i]};
/*表示前i-1种物品中选取若干件物品放入剩余空间为j-k*w[i]的背包中所能得到的
最大价值加上k件第i种物品的总价值*/
else/*放不下的话*/
f[i][j]=f[i-1][j]/*继承前i-1个物品在当前空间大小时的价值*/
}
}
}
//一维
#include<iostream>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int a[50001],b[50001];
int f[50001]={0};
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
}
for(int i=1;i<=n;i++)
for(int j=a[i];j<=m;j++){//循环与01背包不同,正序叠加改变子状态
f[j]=max(f[j-a[i]]+b[i],f[j]);
}
cout<<f[m]<<endl;//最优解
}
- 多重背包
#include<iostream>
//一维实现
using namespace std;
int main()
{
int m,n;
cin>>m>>n;
int a[10001],b[10001],c[10001];
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i];
}
int f[10001];
for(int i=1;i<=n;i++)
for(int j=m;j>=0;j--)
for(int k=0;k<=c[i];k++){
if(j-k*a[i]<0)break;
f[j]=max(f[j],f[j-k*a[i]]+k*b[i]);
}
cout<<f[m]<<endl;//最优解
}
- 输出所选背包(01背包)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100;
int dp[maxn][maxn];
int value[maxn];
int weight[maxn];
int x[maxn];
int main(){
int n,c;
freopen("f.txt","r",stdin);
while(~scanf("%d%d",&n,&c)){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
scanf("%d",&value[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&weight[i]);
}
int i,j;
for(i=1;i<=n;i++){
for(j=weight[i];j<=c;j++){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
printf("Optimal value is\n%d\n",dp[n][c]);
j=c;
for(i=n;i>=0;i--){
if(dp[i][j]>dp[i-1][j]){
x[i]=1;
j-=weight[i];
}else{
x[i]=0;
}
}
for(i=1;i<=n;i++){
if(i==1) printf("%d",x[i]);
else printf(" %d",x[i]);
}
}
}