背包问题

本文深入探讨了背包问题的三种基本类型:零一背包、完全背包和部分背包。通过动态规划方法,寻找在给定重量限制下,如何选择物品以达到最大价值的解决方案。介绍了解决这些问题的思路和初始化状态,并提供了01背包的滚动数组优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背包问题可以说是最经典的动态规划问题了。在这里,我们只讲解最基本的三种背包问题。

零一背包问题:有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。大家可以自己画张表推导一下就明白了。

  1. 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;   //最优解
}
  1. 完全背包
//二维
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;//最优解

}
  1. 多重背包
#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;//最优解
}

  1. 输出所选背包(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]);
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值