0-1背包问题

0-1背包问题

一个正在抢劫商店的小偷发现了n个商品,第i个商品价值pi,重wi磅。这个小偷希望拿走价值尽量高的商品,但他的背包最多能容纳W磅重的商品,W是一个整数。他应该拿走哪些商品??(这个问题是0-1背包问题,因为小偷要么拿走一件商品,要么留下,不能拿走商品一部分,或者把一个商品拿多次)。
0-1背包问题主要问题就是怎么找到最优子结构以及递推式子,我们不妨设f[i,v]为前i个商品,拿走容量为v磅重的商品的最大价值,由此可以得到递归式:
f(i,v) = max{ f(i-1,v), f(i-1,v-w[i])+p[i] }
由上述递归式子我们进行分析,在已知前i-1个商品产生最大价值的基础之上,我们要求前i个商品,拿走容量为v磅商品的最大价值。一样我们就可以分为两种情况,第一种是我们拿了第i件商品,这种情况下最大价值就是第i件商品的价格加上前i-1件商品,拿走容量为v-w[i]磅商品的最大价值;第二种情况是我们没有拿第i件商品,这种情况下最大价值为前i-1件商品,拿走容量为v-w[i]磅商品的最大价值,比较上述两种情况下的最大价值,最大的为我们需要的结果。
理解了该递归式,我们的程序就好写了。
具体程序如下:
#include"stdio.h"
#include"malloc.h"
#define num 3                   //物品数量
#define weight 50               //背包大小
int backpack(int W,int *w,int *p,int n);	//找出最优解
int print(int **m,int *w,int n,int x);		//打印出最优解方案
void main()
{
	int W=50;
	int w[num]={10,20,30};          //物品重量
	int p[num]={60,100,120};        //物品价格
	printf("%d",backpack(W,w,p,n));
	getchar();
}
int backpack(int W,int *w,int *p,int n)
{
	  int f[num][weight+1];	//f[num][weight]代表前num个物品最大承重重量为weight的时候价格的价格
	  int **m;
	  int i,v;
	  m=(int **)malloc(num*sizeof(int));		//为二维指针申请空间
	  for(i=0;i
    
    
     
     (f[i-1][v-w[i]]+p[i]))	//比较选择
					{
						f[i][v]=f[i-1][v];
						m[i][v]=i-1;		//记录当前选择的物品
					}
					else
					{
						f[i][v]=f[i-1][v-w[i]]+p[i];
						m[i][v]=i;
					}
				}	
			}
	  }
	  print(m,w,2,weight);		
	  return f[n-1][weight];
}
int print(int **m,int *w,int n,int x)
{
		int i=n;
		if(n<0)
		{
			return 0;		//终止条件
		}
		if(m[i][x]==i)		//如果拿当前物品,则直接输出
		{
			printf("%d ",m[i][weight]);
			print(m,w,i-1,x-w[i]);
		}
		else		//如果没拿当前物品,则递归下一个
		{
			print(m,w,i-1,x);
		}
		return 1;
}
    
    
在递推式的基础之上,我们还要考虑初始情况,当i=1时(即只有一件物品),当只有一件物品时,容量小于该件物品时,价值为0;当容量大于该件物品时,价值为该物品的价值。在这基础上,我们还要考虑到求第i件商品的最大价值是,当容量小于第i件物品的重量时,我们应该选择第i-1件商品的最大价值作为第i件商品的最大价值。
由上可知,程序的时间复杂度为O(nW),空间复杂度为O(nW),但是由递归式可知,我们只需要f(i-1,v)和f(i-1,v-w)的数据就可以得到下一时刻的值,这样就可以减少我们程序的空间复杂度到O(2W),这种方法可以考虑一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值