01背包问题的理解(蓝桥杯问题--入学考试)

01背包问题是一个典型的dp(动态规划问题),可以理解为将大问题转化为小问题.背包是指你拥有一个空间,可以装下一定体积的物品,每个物品都有各自占的空间v和价值w两个属性.所谓0,1是表示一种状态,表示在这时将不将该件物品放入背包.一般求如何选择得到这个背包里的价值最高.

抽象的讲可能没有概念,可以通过实例来了解:


问题描述

  辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
  如果你是辰辰,你能完成这个任务吗?

输入格式

  第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

  包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例输入

70 3
71 100
69 1
1 2

样例输出

3

数据规模和约定

  对于30%的数据,M <= 10;
  对于全部的数据,M <= 100。


这就是一个很典型的01背包模板题.之前说过01背包基本思想就是对于某个物品选择与否,我们可以用一个二维数组模拟这个过程:

在这里我们可以吧总时间看成一个大背包,采每株草药花费时间为占用背包的空间.

如图,红色的数字表示假设背包大小,从1一直到最大容量70,是将大问题转化为小问题分析的情形,如果我们要求背包容量为70时的背包内的价值,可以求容量为69时它的最大容量,因为我们知道了前面容量的最大价值,当容量增大,我们就可以与前面容量价值作比较,我到底是把原来的大小拿出来换新的价值比较大,还是直接再放东西价值比较大,又或者不移动原来的东西得到的价值比较大.以此类推,我们从容量为0时求起,就可以推到最大的情形.

左边的蓝字是针对这一种东西,我到底该不该选,也就是0,1问题,我把每一种东西的0,1问题都列举,最终数组右下角就是最大价值.

具体思路就是用一个数组w记录草药价值,数组c记录花费时间,将所有数据储存后,便是核心代码:

for(int i=1;i<=m;i++){//m为药草种类
    for(int j=N;j>=w[i];j--){//N为总时间
        dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]);//dp为模拟的二维数组
    }    
}

PS:本来第二层循环是从1到N,进行了优化,从N到w[i]的话,小于w[i]的都可以不用考虑,因为空间比该层物品的所占空间小,肯定放不下,不做考虑.

解读一下核心代码,,第一层for循环是针对上图的红色数字的循环,第二层则是蓝色数字的循环.而后面的dp[i][j]赋值是对于有没有选择当层的物品.dp[i-1][j]是指相等的容量下还没有到这个物品的上一个物品此时背包的价值,这就是没有选择这一层该物品的状态,我没有选择它,所以它还保留上一层的状态,不选择的原因可能是空间不够或者原来价值更高.而dp[i-1][j-c[i]]+w[i]这里的意思是选择这一层的物品放入背包中.j的意义是背包的大小,i的意义是这一层的物品序号,    j-c[i]的意思就是,选择这个物品后还剩的空间,而这个空间可以在二维数组中找到,刚好是之前已经解决了的子问题,刚好就是这一层空间为j-c[i]的价值,dp[i-1][j-c[i]]就是减去该层物品所占空间大小后能放置的物品价值大小,再加上一个w[i]是因为我们有空间放该层的物品,放入后价值也就增加.max()是取最大值的函数,将两者比较就可以求出最大价值,最后dp[i][j]即为所求.

这样就可以求出结果了,但是有一个问题,该题的数据不太大,如果数据过大,二维数组又该开多大?这里,我们会想到优化.而上文二维数组的更新是针对上一行更新的,每次都是针对上一行,那么我们可以尝试就用一个数组,不断将数据覆盖来进行更新.和上面的过程基本相同.

下面就是我的解题代码.

#include<iostream>
using namespace std;
int v[10001],p[10001];
long long dp[100000000];
int main(){
	int N,m;
	cin>>N>>m;
	for(int i=1;i<=m;i++){
		cin>>v[i]>>p[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=N;j>=v[i];j--){
			dp[j]=max(dp[j],dp[j-v[i]]+p[i]);
		}
	}
	cout<<dp[N];
}

以上就是我对01背包模板的理解,依据情况,理解融会贯通,类比,题目大同小异,内核变动不大.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值