PTA 7-3 01背包

事情是这样的,jzk要去爬山,但是他的包容量有限,可是他需要非常多的能量,要不然就很容易饿。 第一行给出jzk准备爬几次山。 每次爬山都会带新的包(因为jzk每用一个包都会被zwg抢过去),和准备新的食物(因为每次剩下来的都被zwg吃了)。 下一行给你这一次食物的数目n,和背包容量k, 接下来的一行给出n个食物的能量,再一行给出n个食物的大小(占背包的容量)。 请帮助jzk计算他最多可以带多少能量的食物去爬山。输出可以携带食物的最大能量和。 (n,m<1000) 能量和食物均小于40000

输入格式:

第一行包含整数T,表示有T组案例。

接着是T组案例,每组案例三行,第一行包含两个整数N,M,(N<=1000,M<=1000),表示物品数量和袋子的体积。第二行包含表示每个物品能量的n个整数。第三行包含代表每个物品体积的n个整数。

输出格式:

每组案例一行,只输出一个数字,表示jzk可以获得的最大能量。

输入样例:

在这里给出一组输入。例如:

1
5 10
1 2 3 4 5
5 4 3 2 1

结尾无空行

输出样例:

在这里给出相应的输出。例如:

14

结尾无空行

TIPS:

(包容量是10)可以带第2,3,4,5,个食物,2 + 3 +4 +5 = 14

背包思路:

(本来用的递归,但是tt了,所以不得不改用递推55555555555555)

       用二维数组其实更好理解,横坐标为当前剩余内存,竖坐标是说明当前行是哪个物品。

然后二维数组里面的值是说明,若是在该剩余内存条件下,最多能装的价值,求二维数组内的值要取决于剩余内存,若剩余内存已经不足以选该行的物品,则该值就等于上一行的值,若是内存够的话,该值要取决于选不选该物体哪个价值更大一点,若是选了该物体,结果就等于内存减去该物品的体积的最大价值加上该物体的价值,若是不选该物体,就等于上一行所代表的没选该物体在该内存下能装的最大价值,两种情况哪种价值大,就留下哪个值。

        但是用二维数组的话内存可能超出限制。所以我用了一维数组,因为某一行只看一次,看完那一次就再也用不到了,所以留着也没啥用,但是要是用一维数组滚动存取的话,需要从内存最大开始循环,因为某一点的值取决于上一行该点的值和上一行前面的某个值。所以后面的值用完之后前面的的值求解是用不到的,所以用完就可以替换掉。

        最后,数组的最后一个值就是最大值,因为它代表的是,该内存下所能装的最大价值,最大内存的最大价值就是我们所要求的。

AC代码:

#include<iostream>
#include<cstdlib>
#include<cstring>

using namespace std;
int *a,*b;
int n,m;
int sum=0;
int q(int m,int i){
	if(i==n){
		return 0;
	} 
	if(m>=b[i]){
		int t=q(m-b[i],i+1)+a[i];
		return max(t,q(m,i+1));
	}
	else{
		return q(m,i+1);	
	}
}
int max(int a,int b){
	if(a>=b){
		return a;
	}
	return b;
}
int main(){
	int T;
	cin>>T;
	for(int qqq=0;qqq<T;qqq++){
//		int n,m;//n是食物数目,m是背包容量 
		cin>>n;
		cin>>m;
		a=new int[n];
		b=new int[n];
		for(int i=0;i<n;i++){
			cin>>a[i];
		} 
		for(int i=0;i<n;i++){
			cin>>b[i];
		} 
//		int **c=new int*[n];
//		for(int i=0;i<n;i++){
//			c[i]=new int[m];
//		}
		int *c=new int[m+1];
		//memset(c,0,sizeof(c));
		for(int i=0;i<m+1;i++){
			c[i]=0;
		}
		for(int j=1;j<n;j++){
			for(int i=m;i>0;i--){
				if(i>=b[j])
				c[i]=max(c[i],c[i-b[j]]+a[j]);
//				else{
//					c[i]=c[i]
//				}
			} 
		}
		cout<<c[m]<<endl;
	}
}