ICPC2021上海区域赛 D.Walker(二分、分类讨论)

  • 题目:Walker
  • 题意:给出两个点a、b在一条线上的位置,并给出两个点的速度,问最少需要多少时间可以将整条线段覆盖(a、b方向任意)
  • 解析:
    1. a或b一人独自走完线段,两者最短时间t1
    2. a、b相向而行,对穿整条线段,两者最长时间t2
    3. 二分一个在a、b之间的点x,a负责区间[0, x],b负责区间[x, n]两者所花费的最大时间t3
    4. 最后比较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;
}

  • 代码2(控制精度方法):
#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;
}

上一篇:LeetCode 剑指Offer 007. 重建二叉树 递归


下一篇:【剑指offer】重建二叉树