- 题目:Walker
- 题意:给出两个点a、b在一条线上的位置,并给出两个点的速度,问最少需要多少时间可以将整条线段覆盖(a、b方向任意)
- 解析:
- a或b一人独自走完线段,两者最短时间t1
- a、b相向而行,对穿整条线段,两者最长时间t2
- 二分一个在a、b之间的点x,a负责区间[0, x],b负责区间[x, n]两者所花费的最大时间t3
- 最后比较t1、t2、t3大小
重点1: 第一种和第二种的情况都挺好想的,主要想总结一下第三种情况,实际上第三种情况包含了非常多的情况,然后这里二分的思想是希望a与b完成相应区间覆盖所花的时间尽可能接近,因为最终是要完成整条线段的覆盖,类似于木桶效应;
重点2: 其次,这里的二分有点卡精度,eps需要开到1e-7,看其他大佬写的题解发现,二分循环100次精度可以到达1e-30???,这样不容易被卡精度。
- 代码1:
#include<bits/stdc++.h>
using namespace std;
int t;
double n, pa, pb, va, vb;
int main()
{
cin >> t;
while(t --)
{
double res = 1000000000;
cin >> n >> pa >> va >> pb >> vb;
if(pa > pb) swap(pa, pb), swap(va, vb); //始终让a在左侧
res = min( (n + min(pa, n - pa)) / va, (n + min(pb, n - pb)) / vb); //a、b独自走完一条线段
res = min(res, max( (n - pa) / va, pb / vb ) ); //a、b相向而行走到边界
// 二分枚举一个点x, a、b负责覆盖x的左侧、右侧
double l = pa, r = pb;
while(r - l > 1e-7)
{
double mid = (l + r) / 2.0;
double ta = min(pa + mid, 2 * mid - pa) / va; //先向左到起点后返回mid/先向右到mid再返回起点
double tb = min(n + pb - 2 * mid, 2 * n - pb - mid) / vb;
res = min(res, max(ta, tb)); //类似木桶效应
if(ta > tb) r = mid; //缩小a的时间
else l = mid; //增加a的时间
}
printf("%.6lf\n", res);
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int t;
double n, pa, pb, va, vb;
int main()
{
cin >> t;
while(t --)
{
double res = 1000000000;
cin >> n >> pa >> va >> pb >> vb;
if(pa > pb) swap(pa, pb), swap(va, vb); //始终让a在左侧
res = min( (n + min(pa, n - pa)) / va, (n + min(pb, n - pb)) / vb); //a、b独自走完一条线段
res = min(res, max( (n - pa) / va, pb / vb ) ); //a、b相向而行走到边界
// 二分枚举一个点x, a、b负责覆盖x的左侧、右侧
double l = pa, r = pb;
for(int i = 1; i <= 100; i++)
{
double mid = (l + r) / 2.0;
double ta = min(pa + mid, 2 * mid - pa) / va; //先向左到起点后返回mid/先向右到mid再返回起点
double tb = min(n + pb - 2 * mid, 2 * n - pb - mid) / vb;
res = min(res, max(ta, tb)); //类似木桶效应
if(ta > tb) r = mid; //缩小a的时间
else l = mid; //增加a的时间
}
printf("%.6lf\n", res);
}
return 0;
}