[贪心]混合溶液

题目描述

n n n 瓶溶液,每瓶溶液体积为 a a a,浓度为 b b b,现在从每瓶中取一些溶液(也可以不选)混合成浓度为 m m m 的溶液,问浓度为 m m m 的溶液体积最大为多少。

输入格式

第一行一个整数 n n n,表示溶液的瓶数。
第二行 n n n 个整数 b i b_i bi,表示每瓶溶液的浓度。
第三行 n n n 个整数 a i a_i ai,表示每瓶溶液的体积。
第四行一个整数 m m m,表示需要混合成的浓度。

输出格式

输出一个小数(保留 5 5 5 位)表示最大的体积。

数据范围

1 ≤ n ≤ 10000 1 \le n \le 10000 1n10000
1 ≤ a ≤ 1000 1 \le a \le 1000 1a1000
1 ≤ b ≤ 500 1 \le b \le 500 1b500

样例

样例输入1

3
50 50 50
395 971 964
50

样例输出1

2330.00000

样例输入2

4
3 5 6 2
9 12 20 18
4

样例输出2

55.50000

题解

由于要是体积最大,我们可以先把所有溶液倒在一起,计算出体积 x x x 和 浓度和体积的乘积 y y y

for(int i = 1; i <= n; ++ i){
	x += x[i].a;
}
for(int i = 1; i <= n; ++ i){
	y += x[i].a * x[i].b;
}

然后比较 x y \frac{x}{y} yx m m m,有以下几种情况:

  1. x y \frac{x}{y} yx 等于 m m m
  2. x y \frac{x}{y} yx 小于 m m m
  3. x y \frac{x}{y} yx 大于 m m m

对于第一种情况,此时体积最大,直接输出。

对于第二种情况,应该从最小浓度的溶液开始倒,直到 x y \frac{x}{y} yx 大于等于 m m m 为止。

  1. 如果 x y \frac{x}{y} yx 等于 m m m,直接输出当前的体积。
  2. 如果 x y \frac{x}{y} yx 大于 m m m,将当前这瓶倒的体积进行实数二分,计算出体积。也可以推导数学公式。
p 表示当前这瓶的编号
double l = 0, r = x[p].a;
while(r - l > 1e-10){
	double mid = (l + r) / 2;
	if(check2(mid, p)){//check2 判断加上 p 瓶 mid 的浓度是否大于 m,大于返回 0
		r = mid;
	}
	else{
		l = mid;
	}
}
sum += l;

对于第三种情况,也是同理。

代码

//要开 long long
bool check(double o, int y){//check 判断加上 p 瓶 mid 的浓度是否大于 m,小于返回 1
	double t = (u + o * x[y].b) / (sum + o);
	if(t - m < 1e-10){
		return 1;
	}
	return 0;
}
bool check2(double o, int y){//check2 判断加上 p 瓶 mid 的浓度是否大于 m,大于返回 0
	double t = (u + o * x[y].b) / (sum + o);
	if(t - m > 1e-10){
		return 0;
	}
	return 1;
}
int main(){
	输入,并计算 u 和 sum
	if(u / sum > m){//浓度大于 m
		浓度从大到小排序
		int p = 0;
		//从最大的开始,依次倒掉,直到浓度小于 m
		while(u / sum > m && p < n){
			sum -= x[++ p].a;
			u -= x[p].a * x[p].b;
		}
		if(u / sum < m){
			double l = 0, r = x[p].a, ans = 0;
			while(r - l > 1e-10){
				double mid = (l + r) / 2;
				if(check(mid, p)){
					l = mid;
				}
				else{
					r = mid;
				}
			}
			sum += l;
		}
	}
	else if(u / sum < m){//浓度小于 m
		浓度从小到大排序
		int p = 0;
		//从最小的开始,依次倒掉,直到浓度大于 m
		while(u / sum < m && p < n){
			sum -= x[++ p].a;
			u -= x[p].a * x[p].b;
		}
		if(u / sum > m){
			double l = 0, r = x[p].a;
			while(r - l > 1e-10){
				double mid = (l + r) / 2;
				if(check2(mid, p)){
					r = mid;
				}
				else{
					l = mid;
				}
			}
			sum += l;
		}
	}
	输出
	return 0;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值