背包——01背包Bone Collector(hdu2602)

首先说明一下01背包


有一个容量为V的背包,另外有N个物品,每个物品Ni都有其对应的体积Vi和价值Pi

求如何取舍装进背包中使得背包里的总价值P达到最大值


题目看似很像贪心问题,把我们不妨先以贪心的思路解决一下这个问题


每个物品都有其对应的价值与体积,我们将其做比,优先选择其比值较高的物品

设:总容量为100,物品N为3

物品A:容量50 价值9K

物品B:容量40 价值7K

物品C:容量55 价值11K

 则贪心的选择顺序为->C->B此时A无法装入,舍弃。 虽然只用了95的总容量,但是其价值确实是最大化,看起来似乎没什么不对。

那么我们再看一组数据

物品A:容量50 价值9K

物品B:容量40 价值11K

物品C:容量65 价值19K

假如我们仍以贪心的思路去选择,我们就会发现,即使C的“性价比”很高,但是我们如果选择了他,就无法再装入其他物品,浪费了容量空间,

还不如选择A+B的价值更大。所以,贪心思想解决不了背包的问题。


问题出在哪里了呢? 对于每一个物品我们都有装和不装两种选择,对于计算机而言,就像是0和1两种状态一样,所以这类问题我们称之为01背包

我们纠结的地方就在于我们单独去看一个物品(亦或几个物品)的时候我们并不知道他到底选还是不选,我们也无法保证当看过n个物品后,我将以

多少剩余容量去面对接下来未知的物品。

所以,我们必须要把看过n个物品之后,所有容量的情况全部考虑进去。



即:外层循环从1到N,用于遍历N个物品,内层循环从0(我们暂且认为没有容量小于0的物品)到V  

即浏览到Ni时,任意一个容量值j的最优价值都计算出来

计算的公式为:当前容量值j如果大于Vi,那么有两种选择——选该物品i,那么当前容量j的价值就变成了j-Vi的最优价值+i的价值,或者是不选择该物品,保留j的价值

即  f(i,j)=max{f(i-1,Pj),f(i-1,P[j-v[i]]+Pi

可能有一些绕,举个例子说明:还是上面的数据,假设我们考虑到C:

那么 对于我每一个容量点(0~100)都要进行是否去物品C的判断,比如说容量50,很明显,装不下,比如说容量90,我要看在我不选物品C之前,我容量80时最大的

价值是多少,如果我选择这个物品C,那么我的价值就是 容量为:90-65=25时我能获取的价值 再加上物品C本身的价值。

不加C时容量90的价值为A+B=20k ,加C为容量25(没有合适的物品,价值为0)+C的价值=0+19=19k,很显然,选择C是一种不合适的行为。


也就是我们的代码为:

for(i=1;i<=n;i++)    //外层循环物品个数
    {
        for(j=0;j<=v;j++) //内层循环每个容量点
        {
            if(j-b[i]>=0)
            dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]]+a[i]);    //容量大于该物品体积时状态转移方程
            else 
    dp[i][j]=dp[i-1][j];          //容量小于该物品体积,只能舍弃

        }
  }


想必到此大家应该明白01背包的解决思路了,下面为hdu一道01背包题


题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2602


题目描述:和01背包基本意思一样, 收集骨头

#include<stdio.h>
	int dp[1020][1020];
int fmax(int a,int b)
{
	return a>b?a:b;
}
int main()
{
	int b[1020];
	int a[1020];
	int t,n,i,j,v;
	
	scanf("%d",&t);
	while(t--)
	{ 
	scanf("%d%d",&n,&v);
    for(i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
	scanf("%d",&b[i]);
	
	for(i=1;i<=n;i++)
    {
        for(j=0;j<=v;j++)
        {
            if(j-b[i]>=0)
            dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]]+a[i]);
            else 
			dp[i][j]=dp[i-1][j];

        }
    
    }
    printf("%d\n",dp[n][v]);
}
return 0;
}









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值