题目大意
给你n个设备和一个充电器,充电器每秒可以充p个单位的电。每个设备(a,b)表示一开始有b的电量,每秒耗电a。充电器可以在任意时刻给任意一个设备充电,问你最多多少秒之后,有一个设备电量为0.(这个最多秒数不一定为整数)
题解
先分析一下题目,问你保证所有设备都能有电的最长时间,那么我们可以想到每台设备如果不充电,会耗电b/a秒。
(I)
假设全部用电器能够永远正常工作,当且仅当
p>=Σmi=1a[i]
。这种情况下输出-1.
(II)
①所以我们按照b/a从小到大拍序。然后看看这些设备能够最多撑多久。
假设现在前m个设备,那么耗电
S=Σmi=1a[i]
。
②假设我们能够撑到第m+1个设备电量为0前,那么我们就可以继续撑下去。我们设用前m个设备还能够继续撑下去
x
秒。能够撑下去,当且仅当
③假设我们撑不下去了,那么答案显然为
tm+x
。
(III)
如果不能够永远正常工作,但是能够撑到所有n个设备充电,那么显然还能够撑
po/[(Σmi=1a[i])−p]
秒。最后答案就是
tn+po/[(Σmi=1a[i])−p]
(显然)。
收获
①这题我个人认为是个贪心,因为它将尽可能多的用电器一起撑,关键是局部解能够反映到最优解。这道题目我们可以画一个解的时间轴:
可以看出局部解反映到最优解。
②为什么我们一定要先给前面的设备充电呢?(尽管这问题很IQ↓~~~~)因为如果你给后面的充,那么前面的设备会先没电,直接影响答案。这个东西的本质其实就是告诉我们在平时解题的时候要分析条件,什么东西影响了答案,在不影响答案的情况下尽量让答案更优。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
#define DB double
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note
{
DB tm;
int a,b;
};note data[N];
DB tm,ans,sum,po,p,tmdis,x;
int i,j,k,l,n,m,t;
bool cmp(note x,note y){return x.tm<y.tm;}
int main()
{
scanf("%d%lf",&n,&p);
fo(i,1,n)
{
scanf("%d%d",&data[i].a,&data[i].b);
sum+=(DB)data[i].a;
data[i].tm=(DB)data[i].b/(DB)data[i].a;
}
if (p>=sum)
{
printf("-1");
return 0;
}
sort(data+1,data+n+1,cmp);
sum=data[1].a;
po=data[1].tm*p;
i=2;
while (i<=n)
{
tmdis=data[i].tm-data[i-1].tm;
if (sum-p>0)
{
x=po/(sum-p);
if (x<=tmdis)
{
printf("%.10lf",x+data[i-1].tm);
break;
}
}
po+=tmdis*(p-sum);
sum+=data[i].a;
i++;
}
if (i==n+1)
{
x=po/(sum-p);
printf("%.10lf",x+data[n].tm);
}
return 0;
}