小鸟的设备
题目背景
小鸟有 $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;
}
标签:样例,mid,小鸟,充电,能量,设备 From: https://www.cnblogs.com/ltphy-/p/18236029