首先,考虑简单情况,两个人的距离太近,就要走远一点,那么两个人可以同时向相反方向走,那么要走距离X花费时间为X/2,而当一个人静止,
另一个人移动,这样两个人远离X要花费时间X,是前一种情况的2倍,这样就可以进行一下转化,即两人相互远离时只需要移动一个人.结果减半
即可.
以上可以推广到N个人, 人的位置任意的情况,即N个人要相互拉开距离,就是固定一个人fixed,其他人都以相邻的靠近fixed的人为参照
进行移动,结果除以2即可.
解题思路陈述:
(1)任意相邻两人的距离都在D范围内:
固定最左的vendor,然后向右迭代,每一个人距其左邻X,右邻就要相对于左邻向右移D-X距离,因为每个人都是相对于其左邻移动,而不受其他人的
影响,所以每一人向右移动所需的时间的和是总共球的时间.
(2),任意相邻两人距离在D范围内,或是INF:
当中间有相邻两个人相距为INF时,左邻无论需要向右移动多远,都不会影响右邻,则右邻不需要向右移动,这时前后就被分割成了互相独立的
组了,组与组之间的成员移动互不影响,对于每组球出一个结果,用最大值来更新最终的结果即可.
(3)相邻两人距离可以取任意值:
这比(2)中多了一种D<X<INF的情况,这时X左邻由于受到其左边的人右移的影响,可能会移动到距右邻小于D的位置,甚至超过右邻,此时如果
仍然套用(2)的思路,就要在每次遇到一个D<X时,维护一个acc积累值,表示左面一组向右移动对右面造成的影响,右面一组需要首先通通向右
移动acc距离,再进行本组的操作,这样就可以使用(2)中方法解题.
附源代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
int P[1000],V[1000];
int array[1100000];
int C,D;
int main()
{
freopen("B-large.in","r",stdin);
freopen("output.out","w",stdout);
int T;
scanf("%d",&T);
for(int loop=1;loop<=T;loop++)
{
scanf("%d%d",&C,&D);
for(int i=0;i<C;i++)
{
scanf("%d%d",&P[i],&V[i]);
}
int cnt=0;
//下面循环将vendor按坐标序写入数组
for(int i=0;i<C;i++)
{
while(V[i])
{
array[cnt++]=P[i];
V[i]--;
}
}
double tmp=0;//记录每组的结果
double res=0;//记录最终结果
int start=0;
double acc=0;//acc记录左面一组对右面一组的影响
for(int i=1;i<cnt;i++)
{
if(D>array[i]-array[i-1]) //距离小于D时
{
tmp+=double(D-array[i]+array[i-1]);//累加到本组的结果中
}
else //距离大于等于D时,
{
if(res<tmp+acc) //用最大值更新res
res=tmp+acc;
tmp=0;
//修改acc,如果左面一组会对右面一组产生影响
if(double(array[i]-array[start])<acc+double(i-start)*double(D))
{
acc=acc+double(i-start)*double(D)-double(array[i]-array[start]);
}
else//没有影响则acc=0
{
acc=0;
}
start=i;
}
}
if(res<tmp+acc)
res=tmp+acc;
res/=2;
printf("Case #%d: %0.10lf\n",loop,res);
}
return 0;
}