专题二 : 01背包 ( HDU 2602 ,UVA 624)

对01背包的理解

01背包的关键部分是“取与不取”
如下:

用两个循环遍历所有情况
for(int i=0;i<N;i++)
			for(int j=0;j<=V;j++) 
				if(j<w[i])//如果背包没位置容纳下目前考虑的物品,则不取
					dp[i+1][j]=dp[i][j];
				else//如果背包能够容纳下目前考虑的物品
				//我们看取了物品和没有取物品,哪种获得的价值更大
					dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);

首先得知道dp数组的意思,dp[ i ][ j ]是指,在考虑前i个物品的时候,如果书包的容量为 j ,此时所能获得的最大价值。

然后分情况讨论:
1、如果现在书包的容量无法容下当前的物品,那么我们肯定是跳过这个物品。
2、如果背包容的下当前的物品,我们看取这个物品更优还是不取这个物品更优。(重点在于这里不要理解错了,刚开始学习的时候可能会觉得:既然背包容的下这个物品,物品又有价值,那肯定是取了更优,其实不是这样的。)

每一个位置(每个i,j)都是当前状态下的最优解(dp[ i ][ j ]所存的值),
所以如果在dp[ i ][ j ]所考虑的第 j 个物品要取,那么取完后肯定不能超过这个背包的总容量(目前容量是 j ),那就看之前的最优解中,放入了这个物品后,背包的重量刚好等于目前背包的总容量i的那个(也就是dp[i][j-w[i]])。然后看加上这个价值后,是否更优,去max的那个。

一开始学习01背包的时候我看不懂,主要是没有理解它的max(dp[ i ][ j ] ,dp[ i ][ j-w[ i ] ]+v[ i ])的意思。

动态规划的题目,你遍历到dp[i][j]处,那么已经遍历过的都是存的相应位置下的最优解。

题目 HDU 2602

题意

给你个背包,有容量。
再给你n个物品,每个物品有两个属性,一个是价格,一个是重量。
要问怎么装这个背包的价值能最大

思路

模板题目,看上面的理解即可。
样例:
1
10 10
1 3 5 7 9 11 13 15 17 19
19 17 15 13 11 9 7 5 3 1

打出来的表:
在这里插入图片描述

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define num 1005
using namespace std;
int N,V;
int v[num],w[num];
int dp[num][num];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(dp,0,sizeof(dp));
		scanf("%d %d",&N,&V);
		for(int i=0;i<N;i++)
			scanf("%d",&v[i]);
		for(int i=0;i<N;i++)
			scanf("%d",&w[i]);
		for(int i=0;i<N;i++)
			for(int j=0;j<=V;j++) 
				if(j<w[i])
					dp[i+1][j]=dp[i][j];
				else
					dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
	/*				
		for(int i=0;i<N;i++)
		{
			for(int j=0;j<V;j++)
				printf("%d ",dp[i][j]);
			printf("\n");
		}
	*/
	}
	return 0;
}

题目 UVA 624

题意

给出的第一个数字是背包容量n。
然后跟你很多个数字,要你从给出的数字里面,取出数字,尽可能让它的和接近n。(并不需要尽可能多的取出数字,就让和尽可能接近n写就能过这道题)
题目中的:
You need to have it on tapes so the problem to solve is: you have a tape N minutes long. How to choose tracks from CD to get most out of tape space and have as short unused space as possible.

这里的 get most out of tape我觉得有歧义。

思路

01背包模板题,取与不取,每次做更优的选择。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,a[25],dp[25][10005];
int judge(int m,int n)
{
	if(m==0)
		return 0;
	//dp[i+1][j]=max(dp[i][j],dp[i][j-a[i]]+a[i]);
	if(dp[m][n]==dp[m-1][n-a[m-1]]+a[m-1])//关键部分 
	{	
		judge(m-1,n-a[m-1]);
		printf("%d ",a[m-1]);
	}
	else
		judge(m-1,n);
	return 0;
}
int main()
{
	while(~scanf("%d %d",&n,&m))
	{	
		memset(dp,0,sizeof(dp)); 
		for(int i=0;i<m;i++)
			scanf("%d",&a[i]);
		for(int i=0;i<m;i++)
			for(int j=0;j<=n;j++)
				if(j<a[i])
					dp[i+1][j]=dp[i][j];
				else
					dp[i+1][j]=max(dp[i][j],dp[i][j-a[i]]+a[i]);
		/*
		for(int i=0;i<=m;i++)
			for(int j=0;j<=n;j++)
				printf("%d ",dp[i][j]);
			printf("\n");
		*/
		judge(m,n);
		printf("sum:%d\n",dp[m][n]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值