题目链接:http://codeforces.com/gym/102900/problem/D
题意:一个坐标轴[0,n]上面有两个点p1,p2,这两个点分别可以以v1,v2的速度向正方向或反方向移动,求轨迹覆盖整个坐标轴的最小时间
解题思路:
一开始想法是多分几种情况
比如:
①一个人走完全程
②两个人相对着走到端点
③两个人先向两端走,然后在中间某点回合
④两个人先往中间走,在某点会和后往两边走
但是其中第三种和第四种分别讨论的子情况太多了,一直wa在第10个样例上
参考了网上的不同的思路,发现可以直接把坐标轴分成两块,让p1和p2去走,然后二分时间来求最小时间就可以了
二分时使用贪心,求p1和p2在t时间内最大的覆盖面积
处理的时候把坐标轴分割为两个区间,分两种大情况,
第一种把左区间分给p1,右区间分给p2
solve(p1, v1, mid) + solve(n - p2, v2, mid)
第二种把右区间分给p1,左区间分给p2
solve(n - p1, v1, mid) + solve(p2, v2, mid)
当取得的覆盖区域超过n的时候即满足条件
之所以要分两种大情况,可以这么想,p1和p2位置都在[0,n/2]上,p1<p2,但是v1远大于v2,所以让p1去跑右区间比较合适
在这两种大情况下,还要分两种小情况,即先往左端点走还是先往右端点走的问题,可以看下方代码注释
double solve(double p, double v, double t) {
//将所有情况都当成p点出发t时间
double d = v * t;
if (d < p)
return 0;
//p点出发t时间的最大覆盖面积s,即覆盖面积为[0,max(s,p)]
//s求法有两种情况,一种是先向左走到端点0再回头,s=d-p
//另外一种是先向右走到最大距离s,再回头走到0,s=p+(d-p)/2
return max(p, max(d - p, p + (d - p) / 2));
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<math.h>
#include<string.h>
#include<string>
#include<map>
using namespace std;
#define ll long long
const double eps = 1e-8;
const int mod = 1e9 + 7;
int t;
double n, p1, v1, p2, v2;
double ans;
double solve(double p, double v, double t) {
//将所有情况都当成p点出发t时间
double d = v * t;
if (d < p)
return 0;
//p点出发t时间的最大覆盖面积s,即覆盖面积为[0,max(s,p)]
//s求法有两种情况,一种是先向左走到端点0再回头,s=d-p
//另外一种是先向右走到最大距离s,再回头走到0,s=p+(d-p)/2
return max(p, max(d - p, p + (d - p) / 2));
}
int main() {
cin >> t;
while (t--) {
cin >> n >> p1 >> v1 >> p2 >> v2;
double L = 0, R = 2e7;
while (L +eps <= R) {
double mid = (L + R) / 2;
//左右区间分割
double s1 = solve(p1, v1, mid) + solve(n - p2, v2, mid);
//右左区间分割
double s2 = solve(n - p1, v1, mid) + solve(p2, v2, mid);
if (s1 >= n || s2 >= n)
R = mid;
else
L = mid;
}
ans = L;
printf("%.6f\n", ans);
}
return 0;
}