P9822 [ICPC2020 Shanghai R] Walker 题解

Description
在一条 
[
0
,

]
[0,n] 的线段上有两名旅行者 

,

A,B,

A 从 

1

1

  开始以每秒 

1

1

  单位的速度运动,

B 从 

2

2

  开始以每秒 

2

2

  单位的速度运动,并且可以随时改变运动方向。请计算出至少需要多少时间,才能使得线段上的每个位置都至少被一名旅行者经过。

Description
不难想到有三种情况(为了方便讨论,我们设 

1


2

1

 ≤p 
2

 ):


(

)
A(B) 独自走完全程,原因是 

(

)
B(A) 走得实在太慢。


A 向右走到底,

B 向左走到底,两人所花的时间取一个最大值。


A 走完左边的一段区间,

B 走完右边的一段区间(两段区间的连接点在 
[

1
,

2
]
[p 
1

 ,p 
2

 ] 内),然后对两人所花的时间取一个最大值。我们钦定这个连接点为 

P,显然他们各自有两种走法:

先走到 

P,再走向边界。
先走到边界,在走向 

P。
不难发现,

,

A,B 的走法互相之间没有影响,不一定全部都要采用同一种走法,只要各自走最优的就行。同时随着 

P 位置的变化,不仅花费的时间会变,他们各自采用的走法也会有所变化,那就不可以简单地将总路程除以总时间再特判一下草草了事。我一开始还以为直接分类讨论一下就可以了。

不过可以肯定的是,随着 

P 的不断右移,不管 

A 采用什么走法,他所花的时间都是单调递增的(总不能我走的路程多了,花的时间反而少了吧);

B 也同理。

同时又想到花费时间可能偏多的本质是 

,

A,B 其中一人在走完自己的区间后另一人还未走完,那么这个人明显可以再多走一点。因此我们考虑二分出一个 

P,使得花费时间最少,即两人恰好走完自己的区间。

最后的答案就在这三种情况里取一个最小值即可。代码时间复杂度 

(

log


)
 
(

=
1
0
11
)
O(TlogP) (P=10 
11
 )。

不得不说这是一道很巧妙的二分题。

Code
#include
using namespace std;
double n, p, v, pp, vv, ans, mid;
int read(){
    int x = 0;
    char a = getchar();
    while(a < '0' || '9' < a) a = getchar();
    while('0' <= a && a <= '9') x = (x << 1) + (x << 3) + (a ^ 48), a = getchar();
    return x;
}
bool check(){
    double t = (min(mid - p, p) + mid) / v, tt = (min(pp - mid, n - pp) + n - mid) / vv;
    if(t < tt){
        if(tt < ans) ans = tt;
        return 1;
    }
    if(t < ans) ans = t;
    return 0;
}
int main(){
    for(int _ = read(); _ >= 1; _ --){
        scanf("%lf%lf%lf%lf%lf", &n, &p, &v, &pp, &vv);
        if(p > pp) swap(p, pp), swap(v, vv);
        ans = min((min(p, n - p) + n) / v, (min(pp, n - pp) + n) / vv); //一人走完
        ans = min(ans, max((n - p) / v, pp / vv)); //互相穿过
        double lft = p, rt = pp;
        while(rt - lft >= 0.0000001){
            mid = (lft + rt) / 2;
            if(check()) lft = mid + 0.0000001;
            else rt = mid - 0.0000001;
        }
        printf("%.6f\n", ans);
    }
    return 0;
}

你可能感兴趣的:(算法)