算法之01背包问题

关于01背包问题:

题目要求:

给定n种物品和一个背包。物品i的重量是wi,其价值是vi,背包的容量是c。问应如何选择装入背包中的物品,使得装入背包中的物品的总价值量最大???  (其中每个物品都只有一个,并且不可以拆分)

 

为什么管这个问题叫做01背包问题呢?? 因为对于每个物品来说,他的情况就只有两种选择,一个是装,令一个是不装。

 

这个问题显然是需要利用最优子结构,并且要利用动态规划的方式进行解决。对于动态规划,必定需要建立一个表格,来保存上次的数据,避免进行重复计算。这里把问题的提出递归式如下:


其中的m, j)的意思是说当容量为j的时候,可以选择的物品为ii+1......n

当容量j大于当前物品的重量Wi的时候,就要看是当前的物品如果不放里面去的 

mi+1, j)大,还是将物品放入背包里面 mi+1, j-Wi+Vi大(这里的i+1的原因是因为当前的物品已经放到里面去了,所以就只能从第i+1个物品开始挑选了,j-Wi的原因是因为物品已经放到背包里面去了, 所以剩下的空间就是j-Wi, +Vi的原因是因为物品已经放进去所以就要把当前这个物品的价值量加上),谁大选那个,当背包的质量不大于当前的物品质量的时候,就只能跳过这个物品了。


所以这里呢就有两种方式进行递推了,一个是逆推,另一个是顺推,我还是比较喜欢逆推,所以这里就说一下逆推的实现方式:

#include <iostream>
#include <cstdio>

#define N  50

int main()
{
	int n;	//定义物品的数量
	int c;	//定义背包的容量
	int w[N];	//定义每个物品的重量 
	int p[N];	//定义每个物品的价值 
	int m[N][N];	//定义记录表格 
	scanf("%d %d", &n, &c);
	
	for(int i=1; i<=n; i++)
	{
		scanf("%d %d", &w[i], &p[i]);
	}
	
	for(int j=1; j<=c; j++)
	{
		if( j >= w[n] )
		{
			m[n][j] = p[n];	//这里为什么是p[n]呢? 开始想了半天,后来看到后面才知道,就像矩阵连乘一样,
							//先把基础的做好,才能往后退,要不怎么叫逆推呢。。。 
		}
		else
			m[n][j] = 0;
	}
	
	for(int i=n-1; i>=1; i--)	//注意这里就体现出了为什么上边是p[n]了, 这里要以上边的为基础开始往会推了 
	{
		for(int j=0; j<=c; j++)
		{
			if( j >= w[i] )
			{
				//这里就是要判断了 
				m[i][j] = m[i+1][j] > m[i+1][j-w[i]]+p[i] ? m[i+1][j] : m[i+1][j-w[i]]+p[i];
			}
		}
	}
	// 输出最大值 
	printf("%d\n", m[1][c]);
}

这里要这种解法有两个缺点,第一个就是当背包的容量很大的时候,就算量就很大了, 再一个就是这种方法的话物品的重量就必须是整数,否则就没法利用数组了,书上大概说了一下利用跳跃点解决这两个问题,我没看太明白,最近比较忙,那种方法等以后有时间再进行更新吧。


然后对于这种问题的升级,就是两个条件的限制了,例如背包的存储的限制有两条:1:承受的质量.2:可以装的最大体积;

虽然有这两条的限制,但是实际对于上边的代码没有太大的改变,只是对上上边进行逆推的数组从两维变成三维即可,第三位就是体积。其他的就依然一样


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值