2020 acm-icpc (上海) D-Walker
题目
链接:https://ac.nowcoder.com/acm/contest/9925#question
示例
输入:
2
10000.0 1.0 0.001 9999.0 0.001
4306.063 4079.874 0.607 1033.423 0.847
输出
5001000.0000000000
3827.8370013755
题目大意
一条长为n的线段上有两个点,位置是p1,p2,他们各有一个恒定的速度v1,v2。你可以随时改变这两个点速度的方向(向左或向右)。问最快在什么时候能让线段上的每一个位置都被最少一个点走过。
思路
首先我们先默认p1<p2 [即,if(p1>p2) swap(p1,p2) ]
那么经过初步的思考,我们会发现可以分为以下3种情况:
1.由走得快的那个人单独走完全程
2.互相走到头
3.在p1,p2之间某点相遇(这里某些具体实现需特判相遇点为端点p1,p2的情况)
那么3种情况取最小值即结果。
具体思路
对于方案1和方案2,可以直接算出,不再赘述。
对于方案3,由于相遇点mid不确定且可能为p1-p2之间的任意位置,枚举所有情况不太现实,因此我们采用二分路程的方法来实现:
初始化令 l = p1,r = p2,mid (l+r)/2;
令t1是p1到相遇点mid的时间,t2是p2到相遇点mid的时间,则t1,t2很好算出。
最终结果ans = min(ans,max(t1,t2))
此时,若t1>t2,我们可以通过左移mid让t1变小,使得ans变小,即r = mid(向左二分)
否则,l = mid(向右二分)。
最后,对于二分次数的问题,由于n<=1000且精度要求1e-6,那么我们只需要做1000次的二分路程就一定能保证精度(r-l)在1e-6以内。(注意10000可能会T)
参考代码
(写得很乱.jpg)
(压行+合并相似的代码可以做到20行左右.jpg)
#include<bits/stdc++.h>
using namespace std;
double n,p1,v1,p2,v2;
double solve1(double p1,double v1,double mid){
if(mid==p1)
return p1/v1; //特判相遇点在端点的情况(其实可以不用)
else{
double d = mid - p1;
double d1 = max(d,p1);
double d2 = min(d,p1);
return (2*d2+d1) / v1; // p1到相遇点mid走过的距离 = 2*短的那段 + 1*长的那段
}
}
double solve2(double p2,double v2,double mid){
if(mid==p2)
return (n-p2)/v2; //solve2与solve1同理
else{
double d = p2 - mid;
double d1 = max(n - p2,d);
double d2 = min(n - p2,d);
return (2*d2+d1) / v2;
}
}
double cal(double p,double v){
double d1 = max(p,n-p);
double d2 = min(p,n-p);
return (2*d2+d1)/v; //一个人走全程需要走的路程
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>p1>>v1>>p2>>v2;
//默认p1<p2,便于后序的讨论
if(p1>p2){
swap(p1,p2);
swap(v1,v2);
}
// ans 即所求结果
double ans = 0x3f3f3f3f;
//互相走到头的情况
ans = min( ans, max ( p2/v2, (n-p1) / v1 ) );
// 一人负责全程的情况
ans = min( ans, min(cal(p1,v1) , cal(p2,v2)));
// 在p1-p2之间相遇的情况(二分路程)
double l = p1,r = p2;
double mid = 0;
int cnt = 0;
while(cnt<=1000){
mid = (l+r)/2.0;
double t1 = solve1(p1,v1,mid); //p1到当前的相遇点mid花费的时间
double t2 = solve2(p2,v2,mid); //p2到当前的相遇点mid花费的时间
ans = min(ans,max(t1,t2));
if(t1>t2)
r = mid; //二分,相遇点左移
else
l = mid; //二分,相遇点右移
cnt++;
}
printf("%.10f\n",ans);
}
return 0;
}