Zombie’s Treasure Chest

对着背影说爱祢 提交于 2019-12-02 15:14:29

Zombie’s Treasure Chest

这道题一看就用了完全背包,结果毫无疑问的挂了,看了一些大佬的解析后,发现有两种做法,一种是直接列举,另一种是通过最小公倍数的方法。

题意

有一个体积为N的箱子和两种数量无限的宝物。 宝物1的体积为S1,价值为V1;宝物2的体积为S2,价值为V2。你的任务是计算最多能装多大价值的宝物。

思路

枚举一:当s1和s2的体积很小,N很大的时候,由题意可知:s2个宝物1和s1个宝物2的体积相等,价值分别为s2v1和s1v2。所以当前者比较大的时候,宝物2最多s1-1个,因为以上的条件可知,当体积相等的条件下宝物1的价值大,所以宝物2不能再超出那个范围了,这样宝物1就不能放到最大化,总价值就不是最大化。后者较大时同理。 所以将俩种宝物都枚举后取最大的。
枚举二:当s1或s2的体积很大(即n/s1 n/s2 很小)时,枚举较大体积的那个宝物即可。

解法一

这些都要注意longlong

//#include <bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include <cmath>
#include <cstring>
using namespace std;
#define LL long long
int t;
LL n,s1,s2,v1,v2;

//求最大公约数
LL gcd(LL a,LL b){
	/*int temp=0;
	while(b!=0){
		temp=a%b;
		a=b;
		b=temp;
	}
	return a;*/
	return b?gcd(b,a%b):a;
} 

LL max(LL a,LL b){
	return a>b?a:b;
}

LL Lcm(LL a,LL b){
	return a/gcd(a,b)*b;
}

int main(){
	cin>>t;
	int Case=0;
	while(t--){
		cin>>n>>s1>>v1>>s2>>v2;
		LL lcm=Lcm(s1,s2);
		LL r=n%lcm,ans=0;
		if(s1<s2){
			LL temp=s1;
			s1=s2;
			s2=temp;
			temp=v1;
			v1=v2;
			v2=temp;
		}
		//容器容量小于两种宝物的最小公倍数 
		//暴力枚举每一种填充的情况 
		if(n/lcm==0){
			for(int i=0;i<=r/s1;i++){
				ans=max(ans,i*v1+(r-i*s1)/s2*v2);//这里为什么是r/s1呢,是因为这些宝石不能被割 
			}
		}else{
			//同样也是暴力枚举,但由于容器容量大于等于最小公倍数,所有还有部分
			//剩余空间可利用,要加上 
			for(int i=0;i<=(r+lcm)/s1;i++)
				ans=max(ans,i*v1+(r+lcm-i*s1)/s2*v2);
			ans+=(n-lcm)/lcm*max(lcm/s1*v1,lcm/s2*v2);
		}
		cout<<"Case #"<<++Case<<": "<<ans<<endl;
	}
	return 0;
} 

解法二

//#include <bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include <cmath>
#include <cstring>
using namespace std;
#define LL long long
int t;
LL n,s1,s2,v1,v2,maxn;
LL max(LL a,LL b){
	return a>b?a:b;
}

int main(){
	cin>>t;
	int Case=0;
	while(t--){
		cin>>n>>s1>>v1>>s2>>v2;	
		//一般把大的放在前面 
		if(s1>s2){
			swap(s1,s2);
			swap(v1,v2);
		}
		LL ans=0;
		//s1s2都很小
		//当n/s1和n/s2都非常大的时候,s1和s2都可以 
		if(n/s2>=65536){//65536是2的16次方,题目要求是适合32位有符号的整数 
			for(LL i=0;i<=s1;i++)
				ans=max(ans,i*v2+(n-i*s2)/s1*v1);
			for(LL i=0;i<=s2;i++)
				ans=max(ans,i*v1+(n-i*s1)/s2*v2);
		}else{//因为数据交换的原因,s2要大一些,所以n/s2要小些,此时枚举宝物2的个数 
			for(LL i=0;s2*i<=n;i++)
				ans=max(ans,i*v2+(n-i*s2)/s1*v2);
		}
		cout<<"Case #"<<++Case<<": "<<ans<<endl;
	}
	return 0;
} 
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!