小鸟的设备

小鸟的设备

题目背景

小鸟有 $n$ 个可同时使用的设备。

题目描述

第 $i$ 个设备每秒消耗 $a_i$ 个单位能量。能量的使用是连续的,也就是说能量不是某时刻突然消耗的,而是匀速消耗。也就是说,对于任意实数,在 $k$ 秒内消耗的能量均为 $k\times a_i$ 单位。在开始的时候第 $i$ 个设备里存储着 $b_i$ 个单位能量。

同时小鸟又有一个可以给任意一个设备充电的充电宝,每秒可以给接通的设备充能 $p$ 个单位,充能也是连续的,不再赘述。你可以在任意时间给任意一个设备充能,从一个设备切换到另一个设备的时间忽略不计。

小鸟想把这些设备一起使用,直到其中有设备能量降为 $0$。所以小鸟想知道,在充电器的作用下,她最多能将这些设备一起使用多久。

输入格式

第一行给出两个整数 $n,p$。

接下来 $n$ 行,每行表示一个设备,给出两个整数,分别是这个设备的 $a_i$ 和 $b_i$。

输出格式

如果小鸟可以无限使用这些设备,输出 $-1$。

否则输出小鸟在其中一个设备能量降为 $0$ 之前最多能使用多久。

设你的答案为 $a$,标准答案为 $b$,只有当 $a,b$ 满足
$\dfrac{|a-b|}{\max(1,b)} \leq 10^{-4}$ 的时候,你能得到本测试点的满分。

样例 #1

样例输入 #1

2 1
2 2
2 1000

样例输出 #1

2.0000000000

样例 #2

样例输入 #2

1 100
1 1

样例输出 #2

-1

样例 #3

样例输入 #3

3 5
4 3
5 2
6 1

样例输出 #3

0.5000000000

提示

对于 $100%$ 的数据,$1\leq n\leq 100000$,$1\leq p\leq 100000$,$1\leq a_i,b_i\leq100000$。

算法1

(二分答案+贪心)

分析

题意大概是,有n台设备和可在任何时刻为任意设备每秒充电p个单位的充电宝。
求电量耗尽前使用时间最短的设备的最长使用时间。

0.为什么用二分:

需要找到充电宝的最大使用时间-最值问题

1.二分什么:

使用时间

2.二分边界:

左边界-l=0; 有边界:r=le10;

因为有1e5个设备,每一个设备最多是1e5个能量,所以右边界就应该是 1e5×1e5=1e10;

3.判断依据:

我们用二分找到 mid 值表示最长使用时间
当前mid值时
(1)所有设备需要充的电量<=充电宝的电量 当前时间足够用,把时间调大点
(2)所有设备需要充的电量>充电宝的电量 当前时间超出了充电宝能工作的时间 把时间调小

4.贪心策略

首先,如果一个设备在t的时间内消耗的能量小于或等于原有的能量,那么我们自然没有必要浪费时间给它充电,因为它在这段时间内的能量不会降为 0 。

然后我们考虑设备消耗的能量大于原有能量的情况。

由于我们并不在乎什么时候给设备充电,所以我们只需要对于每一个这样的设备,求出我们需要给它充的电即可。
求出需要的电的方法显然,就是 a[i]*t-b[i]
然后我们只需要把所有的需要充电的设备需要充的电加起来,判断充电宝能否在 t 的时间内充这么多的电即可。

时间复杂度 $O(nlogn)$

空间复杂度 $O(n)$

参考文献

例如数据:

3 10

5 5

5 5

5 5

答案为:3。按照我代码里的意思是每个手机用了 1s 来充电让他能用到 3s。那么问题就来了,三把手机在 1s 就都没电了,你只给一把充了电,其他的还是没电了,答案不应该是 1 吗?

不是的!我们的充电并不是一次性充了 b[i]-a[i]*x,而是充了多次,他的充电时间和为这个值!为什么可以这样做呢?

首先换手机充电的操作是瞬间完成,其次电量变化是连续的。这也就给了我们充一会一把手机,让它能用到我把其他手机充电到能用到相同时刻的电量的可能。这样我们就可以一直按这样做下去,使其使用时间尽量长。因为使用时间可以二分找到,那么我们对于每把手机的充电量也就可以全部一次性算出来,而不用一份一份算啦。

所以我们在 check函数里判断时间可不可行虽然是整段算的,但实际上它被分为很多细小的部分分别进行充电。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N=100005;

double n,p,a[N],b[N];

bool check(double mid)
{
     double sum=0;
     for(int i=1;i<=n;i++){
     	if(a[i]*mid > b[i])
     	sum+=a[i]*mid-b[i];
	 }
	 return sum <= p*mid;
}
int main(){
	cin>>n>>p;
	
	for(int i=1;i<=n;i++)
	cin>>a[i]>>b[i];
	
	double tmp=0;
	for(int i=1;i<=n;i++){
	 tmp+=a[i];
	}
	if(tmp<=p) {
		cout<<"-1";
		return 0;
	}
	double l=0,r=1e10+1;
	
	while(r - l >=0.00001){
		
		double mid = (l+r)/2;
		
		if(check(mid)) l=mid;
		else r=mid;
	}
	cout<<l;
	return 0;
} 

posted @   LTphy  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示