← Все вопросы

Бинпоиск по ответу с вещественным предикатом: задача про максимальное среднее подотрезка

Задан 2 месяца назад1.2к просмотров2 ответа
11

Задача: найти подотрезок длины не меньше L с максимальным средним значением. Перебор всех подотрезков O(n^2). В разборе — «бинпоиск по ответу», но среднее вещественное, и я не понимаю, как тут построить предикат.

2 ответа

16
✓ Принятый ответ — помог автору

Это бинпоиск по вещественному ответу x = «среднее». Предикат: check(x) = «существует ли подотрезок длины >= L со средним >= x». Монотонность: если такой подотрезок есть для среднего x, то и для любого меньшего x' < x он тоже годится (то же среднее >= x > x'). Значит предикат true...true false...false по x — ищем максимальный истинный x.

Ключ — как проверять check(x). Среднее подотрезка >= x(сумма)/(длина) >= xсумма(a[i]) - x*длина >= 0 ⇔ сумма по подотрезку из b[i] = a[i] - x неотрицательна. Значит вопрос сводится к: «есть ли в массиве b подотрезок длины >= L с суммой >= 0».

Это решается за O(n) через префиксные суммы pref массива b и минимум префикса с отставанием L: для каждого r (r >= L) проверяем pref[r] - min(pref[0..r-L]) >= 0. Минимум поддерживаем на лету.

bool check(const vector<double>& a, int L, double x) {
    int n = a.size();
    vector<double> pref(n + 1, 0);
    for (int i = 0; i < n; i++) pref[i + 1] = pref[i] + (a[i] - x);
    double minPref = 0; // min(pref[0..r-L])
    for (int r = L; r <= n; r++) {
        minPref = min(minPref, pref[r - L]);
        if (pref[r] - minPref >= 0) return true;
    }
    return false;
}

Внешний бинпоиск — по числу итераций (100 штук) в [minA, maxA]. Общая сложность O(n * iters) ≈ O(100 n), память O(n). Это классический трюк «бинпоиск по дробному ответу + сведение к сумме через вычитание кандидата».

6

Грабля, на которой ловят WA: правильно поддерживать minPref именно по префиксам с отставанием L (pref[0..r-L]), а не по всем pref[0..r] — иначе разрешишь подотрезки короче L. Добавляй pref[r-L] в минимум ровно тогда, когда правый конец дорос до r. И границы бинпоиска: lo = min(a), hi = max(a) (среднее любого подотрезка лежит между min и max элемента). 100 итераций дают точность ~ (max-min)/2^100, чего хватает с запасом; если в задаче просят точность 1e-6, можно остановиться и на ~60 итерациях.

Ваш ответ

Войдите, чтобы ответить на вопрос.