从CF1730B看题意转化与二分三分

Problem - 1730B - Codeforces

贪心解法

\(∣a−b∣=\max(a−b,b−a)\)

由绝对值的性质易证。

那么直接把 \(t_i\) 算到距离中,转换成求最左和最右“坐标”的中间点的简单问题。

//通过把t[i]算到距离中,转换成求最左和最右坐标的中间点的简单情况

void solve()
{
    #define tests
    int n;
    std::cin >> n;

    std::vector<int> x(n), t(n);
    for (int i = 0; i < n; i++) std::cin >> x[i];
    for (int i = 0; i < n; i++) std::cin >> t[i];

    std::vector<int> a;
    for (int i = 0; i < n; i++) {
        a.push_back(x[i] + t[i]);
        a.push_back(x[i] - t[i]);
    }

    int mn = a[0], mx = a[0];
    for (int val : a) {
        mn = std::min(mn, val);
        mx = std::max(mx, val);
    }

    int sum = mn + mx;
    std::cout << sum / 2;
    if (sum % 2 != 0)
        std::cout << ".5";
    std::cout << '\n';
}

二分解法

\(x_0\) 进行二分,判断左边和右边谁到达 \(x_0\) 的最大时间更大

如果左边距离更大,就往左边靠近继续二分

void solve() {
    #define tests
    int n;
    scanf("%d", &n);
    
    std::vector<i64> a(n);
    std::vector<i64> t(n);

    for (auto& x : a) scanf("%d", &x);
    for (auto& x : t) scanf("%d", &x);
    
    double lo(0), hi(*std::max_element(all(a))); 
    
    int res(0);

    auto check = [&](auto X) -> bool {
        double lMax(0), rMax(0);
        for (int i = 0; i < n; i++) {
            if (a[i] < X) {
                lMax = std::max(lMax, X - a[i] + t[i]);
            }
            else {
                rMax = std::max(rMax, a[i] - X + t[i]);
            }
        } 
        return rMax < lMax;
    };

    constexpr double eps(1E-7);

    while(hi - lo > eps) {
        double mid((lo + hi) / 2.0);
        if (check(mid)) {
            hi = mid;
        }
        else {
            lo = mid;
        }
    }

    printf("%.6lf\n", hi);
}

三分解法

三分法适用于给出 \(N\) 个函数,保证在范围内存在一点 \(x\),使得左边界 \(x\) 上单调增,\(x\) 到右边界上单调减。

可以用三分求出 \(\max(f_1(x), f_2(x), f_3(x), \dots, f_n(x))\) 取最小时,对应的 \(x\) 的值。

而这题的绝对值函数 \(T_i = t_i + \left | x_i - x_0 \right |\) 满足条件,所以直接对 \(x_0\) 进行三分。

int _, n, a[N], t[N];

double f(double x, LL i) {
    return (fabs(a[i] - x) + t[i]);
}

double check(double x) {
    double res = f(x, 1);
    for (int i = 2; i <= n; i++) {
        res = max(res, f(x, i));
    }
    return res;
}

void solve() {
    cin >> n;
    for (LL i = 1; i <= n; i++) cin >> a[i];
    for (LL i = 1; i <= n; i++) cin >> t[i];
    double m1, m2, l = 0, r = 0x3f3f3f3f;
    while (r - l > eps) {
        m1 = l + (r - l) / 3.0;
        m2 = r - (r - l) / 3.0;
        if (check(m1) > check(m2)) {
            l = m1;
            // 如果m1对应的函数最大值比m2对应的值要大。
            // 则说明左边界l需要收敛
        }
        else {
            r = m2;
            // 如果m2对应的函数最大值比m1对应的值要大。
            // 则说明右边界r需要收敛
        }
    }
    printf("%.7lf\n", l);
}
posted @ 2024-03-12 17:32  加固文明幻景  阅读(10)  评论(0编辑  收藏  举报