经典背包问题

01背包问题

题目:洛谷P1048
AC代码:

#include<stdio.h>
int t[110],v[110];
int dp[1010];
int max(int a,int b)
{return a>b?a:b;}
int main()
{
	int T,M;
	scanf("%d%d",&T,&M);
	int i,j;
	for(i=1;i<=M;i++)
	  scanf("%d%d",&t[i],&v[i]);
	for(i=1;i<=M;i++)
	  for(j=T;j>=t[i];j--)
	    dp[j]=max(dp[j],dp[j-t[i]]+v[i]);
	printf("%d",dp[T]);
	return 0;
}

01背包问题思路分析
第i重循环中的dp[j]表示使用前i个物品,在j空间下能获得的最大价值。
对于第i个物品,只有 用 或 不用 两个选择,应选择能使价值最大的一个。
如果不用,j空间的背包还是能装之前i-1个物品中最大的那个值;如果用,j空间的背包中最大值为第i个物品的值加上j-t[i]空间中能装的最大值。

完全背包

题目:洛谷P1616
AC代码:

#include<stdio.h>
#define ll long long
ll t[10010],v[10010];
ll dp[10000010];
ll max(ll a,ll b)
{return a>b?a:b;}
int main()
{
	ll T,M;
	scanf("%lld%lld",&T,&M);
	int i,j;
	for(i=1;i<=M;i++)
	  scanf("%lld%lld",&t[i],&v[i]);
	for(i=1;i<=M;i++)
	  for(j=t[i];j<=T;j++)
	    dp[j]=max(dp[j],dp[j-t[i]]+v[i]);
	printf("%lld",dp[T]);
	return 0;
}

完全背包就是在01背包的基础上把内循环的倒序改成了正序
倒序巧妙地使每一个物品只能用一次
比如i=1时,即只用第一个物品时,从T到t[i],都是建立在之前背包为0的基础上,即这个物品只能用一次。
而正序时,从t[i]到T,j=t[i]时可以放一个物品,j=2*t[i]时,之前的dp[j-t[i]]已经有一个物品了,正序时是在之前已经放过这个物品的基础上继续看放还是不放。所以倒序改正序即可。

多重背包

经典的二进制转换法
题目:洛谷P1776
AC代码:

#include<stdio.h>
int v[1000005],w[1000005];//v为价值,w为重量
int dp[1000005];
int max(int a,int b)
{return a>b?a:b;} 
int main()
{
	int n,W;
	scanf("%d%d",&n,&W);//n为宝物种数,W为最大载重 
	int i,j,cnt=0;
	int a,b,c; 
	for(i=1;i<=n;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		for(j=1;j<=c;j*=2)
		{
			v[++cnt]=j*a;
			w[cnt]=j*b;
			c-=j;
		}
		if(c!=0)
		{
			v[++cnt]=c*a;
			w[cnt]=c*b;
		}
	}
	for(i=1;i<=cnt;i++)
	  for(j=W;j>=w[i];j--)
	    dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	printf("%d",dp[W]);
	return 0;
}

多重背包思路就是转化为01背包。因为数量是固定的,我们很容易想到的思路就是把数量为n的一种物品转化为n个物品来做01判断。但是这样数量太大,会超时。
一种特殊的办法是二进制转化,也就是对数量为n的物品,把它转化成1+2+22+…+2k+剩下的。这样对1~n的每一个数,都能由上面的某些数组合出来。
即把n个物品打包成若干包,每一包数量为上面的数列,每一包算一个物品。这样,不管想选择几个物品放入,都能等价地转化为选择某些包组合出这个数来。
这样就把多重背包转化为了对这些小包的01背包问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值