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;
}