背包九讲

目录

  1. 01背包
  2. 完全背包
  3. 多重背包
  4. 混合背包
  5. 分组背包
  6. 二维费用背包
  7. 有依赖背包
  8. 背包问题的方案及方案数

1.01背包:

问题描述:有n种物品,每个物品的数目是1,价值是w,体积是v,有一个容量V的书包,问书包所能装的最大价值是多少?

  • dp [ i ] = max( dp [ i ] , dp [ i - v[ i ] ] + w [ i ] )

从大到小枚举,原因:一维的转移方程实际上是由二维演变过来,对于每个物品,有取和不取两种取法,当枚举到第i个物品时,他只由dp [ i-1]转移,所以说,前面的dp(1~i-2)是不需要的,原理如图:

2.完全背包:

问题描述:有n种物品,每种物品的数目是无限个,价值是w,体积是v,有一个容量V的书包,问书包所能装的最大价值是多少?

  • dp [ i ] = max( dp [ i ] , dp [ i - v[ i ] ] + w [ i ] )

从小到大枚举,理由如上图,如果从小到大枚举相当于之前的状态可多次选取。

3.多重背包

问题描述:有n种物品,每种物品的数目s,价值是w,体积是v,有一个容量V的书包,问书包所能装的最大价值是多少?

二进制拆分,拆分出来的数可以组成,0~这个数之间的所有数。然后问题就转换为01背包了。

using namespace std;

vector<pi>ans;
int dp[1010];
int main()
{
	int n,V;
	cin>>n>>V;
	for(int i=1;i<=n;i++)
	{
		int v,w,s;
		cin>>v>>w>>s;
		for(int k=1;k<=s;k*=2)
		{
			s-=k;
			ans.pb(mk(k*v,k*w));
		}
		ans.pb(mk(s*v,s*w));
	}
	for(auto it : ans)
	{
		for(int i=V;i>=ans.first;i--)
		dp[i] = min(dp[i],dp[i-ans.first]+ans.second);
	}
	cout<<dp[V]<<'\n';
	return 0;
} 

4.混合背包:

问题描述:前面三种物品的混合。

首先把多重背包二进制拆分成01背包存到vector中,那么此时物品种类只有两种,枚举物品,如果这个物品是01背包,从大到小枚举,否则从小到大枚举。

using namespace std;

struct node {
	int type,v,w;
};
vector<node>ans;
int dp[1010];
int main()
{
	int n,V;
	cin>>n>>V;
	for(int i=1;i<=n;i++)
	{
		int v,w,s;
		cin>>v>>w>>s;
		if(s == -1)ans.pb({-1,v,w});
		else if(s == 0)ans.pb({0,v,w});
		else
		{
			for(int k=1;k<=s;k*=2)
			{
				s-=k;
				ans.pb({-1,v*k,w*k});
			}
			ans.pb({-1,s*v,s*w});
		}
	}
	for(auto it : ans)
	{
		if(it.type == -1)
		{
			for(int i=V;i>=it.v;i--)dp[i] = max(dp[i],dp[i-it.v]+it.w);
		}
		else 
		{
			for(int i=it.v;i<=V;i++)dp[i] = max(dp[i],dp[i-it.v]+it.w);
		}
	}
	cout<<dp[V]<<'\n';
	return 0;
} 

5.分组背包:

每组物品有若干个,组内物品互斥,每组中的只能选择一个,求背包的总价值。

三重循环,枚举每一组中的所有物品,对于这组内的每个物品,都由上一组转移。

所以过程就是,首先枚举组,然后枚举转移的状态,然后枚举组内的物品,复杂度是n^3.

using namespace std;
vector<pi>G[105];
int v[1005],w[1005],d[1005];
int main()
{
    int V,n,mm=0;
    scanf("%d%d",&V,&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>v[i]>>w[i]>>x;
        G[x].push_back(mk(v[i],w[i]));
        mm=max(mm,x);
    }
    for(int i=1;i<=mm;i++)
    {
        for(int j=V;j>=0;j--)
        {
            for(auto it : G[i])
            {
                if(j>=it.first)d[j]=max(d[j],d[j-it.first]+it.second);
            }
        }
    }
    cout<<d[V]<<'\n';
}

6.二维费用背包:

问题描述:背包的限制不止有体积,还有重量。

拓展维度,01背包从大到小枚举两个限制,完全背包从大到小。

using namespace std;

int dp[1010][1010];
int main()
{
	int n,V,M;
	cin>>n>>V>>M;
	for(int i=1;i<=n;i++)
	{
		int v,m,w;
		cin>>v>>m>>w;
		for(int j=V;j>=v;j--)
			for(int k=M;k>=m;k--)
				dp[j][k] = max(dp[j][k],dp[j-v][k-m]+w);
	}
	cout<<dp[V][M]<<'\n';
	return 0;
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值