51Nod - 1548 欧姆诺姆和糖果(枚举,根据题意缩小范围题)

一天,欧姆诺诺姆来到了朋友家里,他发现了许多糖果。有蓝色和红色两种。他知道每颗红色糖果重Wr克,每颗蓝色糖果重Wb克。吃一颗蓝色糖果会给他带来Hb的欢乐值,吃一颗红色糖果会给他带来Hr的欢乐值。

欧姆诺姆最多只能吃C克的糖果,而且每一颗糖果不能只吃一半。现在他想通过吃蓝色和红色的糖果来获得最大的欢乐值。

样例解释:每一种糖果吃两颗即可。


Input单组测试数据。 
输入占一行有四个整数C,Hr,Hb,Wr,Wb (1≤C,Hr,Hb,Wr,Wb≤10^9).Output输出最大可能获得的欢乐值。Sample Input
样例输入1
10 3 5 2 3
Sample Output
样例输出1
16

题意:中文题,题意就不说了;

思路:刚开始看题时,我想大家可能和我一样都是,先想到的是dp,完全背包吧,但是你在一看数据范围,打消这个念头;我又认真看题,找到wr和wb的最小公倍数,因为只有当他们重量一样多时,看看谁提升的提升欢乐值多,让c-c%他们的最小公倍数,这部分用提升欢乐值大的一方,其余的 c%最小公倍数 这部分枚举,枚举其中一个的数量,因为根据题意可以列来二元一次方程组,但是其中一个是不等式,这样的话,只要知道其中一个的数量,另一个数量就是确定的,枚举其中一个的数量,找欢乐值最大的结果,但是枚举的范围又该怎么确定啊?

数据范围为10^9,你要想用枚举的话,显然要优化到 根号c 或 log 级别的运算

这时候我们就该缩小其中一个数量的范围了,设重量为wr的为x个,重量为wb的为y个,方程 wr*x + wb*y = c,

情况一:当 wr的重量大于等于sqrt(c) 的话,那么枚举x,范围为0 ~ sqrt(c);

情况二:当wb的重量大于等于sqrt(c)的话,那么枚举y,范围为0 ~ sqrt(c);

情况三:当wr和wb的重量都小于sqrt(c)的话,wr*wb <= c。我们选wb个r和wr个b,这时候重量是一样的,但收益 一个是 wb*hr  另一个为wr*hb,这时候假设收益较小的为 r的话,那么r的数量一定不超过wb,否则就可以整体换成b了  (这里其实和我上面找wr和wb的最小公倍数的思路是相似的) ,所以r的数量 < wb < sqrt(c);

所以整体时间复杂度为 sqrt(c);对了,在代码中还有一些数据处理,代码中会有解释

代码:

#include<stdio.h>         
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef long long ll;
	
ll c;

ll fff(ll k,ll w1,ll w2,ll h1,ll h2)   // w1是要枚举的对象; 
{
	ll i,j;
	ll Max_ans = 0;
	for(i=0;i<=k;i++)
	{                           // 数据范围要用longlong因为w1*i可能会大于int型 
		if(w1*i<=c)            // 一开始我是加的下面y<0时的条件;一直看不出和这个条件有啥区别; 
		{						// 现在我知道了,-1/2 可能在我们的印象中的结果为负数,但是在计算机中它的结果是0;			
			ll y = (c - w1*i)/w2; // 所以你必须要用上面的那个式子,来判断有没有超过c的范围; 
			//if(y<0)
				//continue; 
			Max_ans = max(Max_ans,h1*i+h2*y);
		}
		
		
	}
	return Max_ans;
}

int main()
{
	ll i,j;
	ll hr,hb,wr,wb;
	while(~scanf("%lld%lld%lld%lld%lld",&c,&hr,&hb,&wr,&wb))
	{
		ll k = (ll)sqrt(c*1.0);
		if(wr>k)                        // 情况一 
			printf("%lld\n",fff(k,wr,wb,hr,hb));
		else if(wb>k)                    // 情况二 
			printf("%lld\n",fff(k,wb,wr,hb,hr));
		else                             // 情况三 
		{
			double t1 = (hb*1.0)/(wb*1.0);
			double t2 = (hr*1.0)/(wr*1.0);
			if(t1>t2)
				printf("%lld\n",fff(k,wr,wb,hr,hb));
			else printf("%lld\n",fff(k,wb,wr,hb,hr));
		} 	  
	}
	return 0;
}

法二:

#include <bits\stdc++.h>
using namespace std;
int main()
{
    long long ans, c, wr, wb, hr, hb;
    int lala = 10000000;
    while(~scanf("%lld%lld%lld%lld%lld", &c, &hr, &hb, &wr, &wb))
    {
        ans = 0;
        for(long long i=0;i<lala;++i)
        {
            if(i*wr<=c)
                ans = max(ans ,i*hr+(c-i*wr)/wb*hb);
            if(i*wb<=c)
                ans = max(ans ,i*hb+(c-i*wb)/wr*hr);
        }
        printf("%lld\n",ans);
    }
 } 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值