Как стать автором
Обновить

Комментарии 23

2
100%. Хотя это и кажется на первый взгляд странным, но ответ становится очевидным, если представить, что два человека выехали в один день навстречу друг другу и ехали по одной дороге с разной скоростью. Они обязательно встретятся.
Тут же вроде вопрос в том, когда встретятся. В задаче условие «at the same time on both the days»

Тут вопрос о вероятности, а не о времени.
Время — любое в интервале 7-12 часов (ибо скорости не постоянные), а вероятность 100%

Не соглашусь. Понятно что вероятность встречи 100%, но в задаче не просто так расписаны все времена, плюс условие задачи вполне четко сформулировано: «в одной и той же точке в одинаковое время».

Именно что не просто так. А для того, чтобы народ повёлся, начал искать глубинный смысл и высчитывать вероятность по безумным формулам.
Тогда как загадка — на смекалку.

1 вопрос
45 минут.
Примем объем ванной за N литров, и выразим скорости прибывания горячей как N/18, холодной как N/15, а убывания как N/10. Тогда итоговая скорость наполнения будет N/18 + N/15 — N/10 = 2N/90. Отсюда время наполнения N/ (2N/90) = 45 минут
Вопрос 1 - классическая задача о бассейне и трубах для 6 класса
Первый кран заполняет ванну за 15 минут, значит за минуту он выдаёт 1/15 от объёма ванны. Второй кран — 1/18 от объёма, сток за минуту сливает 1/10 объёма.
В сумме за минуту заполнится 1/15 +1/18 — 1/10 = 1/45, значит вся ванна заполнится за 45 минут.

Вопрос 2
Достаточно представить, что не один человек едет в разные дни, а два человека едут одновременно навстречу друг-другу. Очевидно, что в месте встречи они окажутся одновременно.

В задаче 1 нестыковка — если мы можем нажать только кнопки выше, ниже, левее или правее текущей, то как получаются комбинации 00, 11, 22 и т.д.? Ведь сама текущая кнопка под это определение не подходит.
вопрос 1
1/(1/15 + 1/18 - 1/10)
= 1/(1/(3*5) + 1/(2*3*3) - 1/(2*5))
= (2*3*3*5) / (2*3 + 5 - 3*3)
= 90 / 2
= 45

В задаче 2 непонятно — можно нажимать только кнопки в 1-окрестности от заданной?
Т.е. если задана кнопка 0 и длина 3, то можно получать {'0','8'}*3?


Если так, то задача тупая. Для каждой кнопки мы знаем основание степени:
0 — 2
1 — 3
2 — 4
3 — 2
и т.д.
И возвести это основание в показатель длины.


Если же имеется в виду, что можно нажимать эту-и-соседние кнопки, то задача будет о количестве путей заданной длины в графе.
В этом случае берём матрицу смежности и возводим в N-ную степень в кольце (макс,+)...

Задача 2 про дерево

Если у нас есть O(N) дополнительной памяти и не жалко рекурсивно спускаться, то заводим табличку "оригинал-копия".


std::map<node*, node*> copies;
node* clone(node* original) {
  if (!original) return NULL;
  node*& copy = copies[original];
  if (copy) return copy;
  copy = new node;
  // принципиально, что мы сперва создаём и регистрируем копию в таблице,
  // а уже потом рекурсивно спускаемся (возможно, попадая на самих себя!)
  node->key = original->key;
  node->left = clone(original->left);
  node->right = clone(original->right);
  node->random = clone(original->random);
  return node;
}

Если жалко, то придётся изгаляться.


Например, если у нас есть гарантии того, что это именно дерево, то поступим следующим разрушающим образом:
1) построим двудольное дерево: нечётный слой (оригинал) ссылается на чётный (копию) через random, а чётный (копия предыдущего) — на следующий нечётный оригинал.
2) расслоим дерево: нечётные слои ссылаются на нечётные, чётные — на чётные


void duplicate(node* o) {
  if (!o) return;
  node* c = new node(o);
  o->random = c;
  duplicate(c->left);
  duplicate(c->right);
}

void separate(node* o) {
  node* c = o->random;
  c->left = c->left ? c->left->random : NULL;
  c->right = c->right ? c->right->random : NULL;
  node* r = c->random;
  c->random = c->random ? c->random->random : NULL;
  o->random = r;
  separate(o->left);
  separate(o->right);
}

node* copy(node* root) {
  if (!root) return NULL;
  duplicate(root);
  node* c = root->random;
  separate(root);
  return c;
}

Вроде бы, не налажал.

В задаче 1 непонятно — 2 крана на смесителе или на отдельных трубах? Потому как если на смесителе, то подобные расчеты будут не верны. Также не понятно и то, происходит ли спад давления в системе если открыты оба крана?
Задача 3:
Вычисляем среднее количество страниц которые нужно читать каждый день. Читаем главу, смотрим на количество страниц следующей главы, если прочитав следующую главу читатель отклонится от среднего количества страниц к прочтению больше чем в данный момент, читатель останавливается. В противном случае — продолжает читать.

Пример 2: {8, 5, 6, 12} — среднее количество страниц 10,(3)
Д1 — Читаем 8 страниц (отклонение 2,(3)), если прочитаем ещё 5 (8 + 5 = 13) получим отклонение 2,(6), что больше, значит нужно остановится.
Д2 — Читаем 5 страниц (отклонение 5,(3)), если прочитаем ещё 6 получим отклонение 0,(6), значит продолжаем, если прочитаем ещё 12 получим отклонение больше, значит нужно остановится.
Д3 — Читаем оставшееся.

Пример 3: {8, 5, 10, 12} — среднее количество страниц 11,(6)
Д1 — Читаем 8 страниц (отклонение 3,(6)), если прочитаем ещё 5 (8 + 5 = 13) получим отклонение 1,(4), значит читаем дальше, если прочитаем ещё 10 страниц получим большее отклонение значит останавливаемся.

Метод не сработает при {20,1,1,1} и k=3. Все оставшиеся страницы будут прочитаны в 1 день. Нужно наверное корректировать среднее на оставшиеся дни.

При {1,1,1,30}, k = 3 и корректировка среднего не поможет, три главы будут прочитаны в первый день, последняя во второй.
А это и не нужно. «Finish the book in 'k' days» значит, что через «k» дней книга должна быть прочитана, но отнюдь не значит, что она не может быть прочитана быстрее. Поэтому, если параметр оптимизации — это уменьшение дисперсии количества страниц в день, правильный ответ везде будет «глотать книгу целиком». Вот если там появится параметр «минимизировать ещё и среднее количество страниц в день», тут возможны варианты. И если честно, для {20,1,1,1} правильный ответ все-таки прочесть за день, негоже три страницы оставлять на второй (да и просто дисперсия великовата).
Но по условию задачи «A person is determined to finish the book in ‘k’ days». Не "`k` or less". То есть суть именно в том, чтобы правильно разбить массив на k частей, как я понял.
Задача 2 — ключи уникальные или нет? А то корректно не выйдет склонировать, если за random разных нод спряталось два разных идентичных поддерева. Хотя если сравнивать указатели, то вариант таблицы «оригинал — копия» как у nickolaym выше, пройдет.
Ключи могут быть неуникальными.

К задаче 3.
Как точно сформулировать критерий оптимальности? Чтобы минимизировать максимальное отклонение от среднего количества страниц, или чтобы минимизировать среднеквадратичное отклонение, к примеру?
Кажется, можно придумать набор данных, при котором результаты по этим критериям будут различаться.

Думаю, нужно минимизировать максимальное отклонение.

Подумал и понял, что нам пофиг на вид целевой функции.
Лишь бы она была (тут я забыл линейную и абстрактную алгебры, как называется класс таких структур...).


Концепция решения задачи 3

Это задача на динамическое программирование!


Пусть у нас N глав по p0, p1, p2, ..., p[N-1] страниц, итого P страниц, и K дней.
(Если N <= K, то решение тривиально)


В среднем выходит M = P/K страниц в день — это базовая линия.


В первый день прочитали 0..N глав (на самом деле, 1..N-K+1 — если требовать не менее 1 главы в день).
Это s[k] = 0 + p0 + p1 +… + p[k-1] страниц в сумме.
Что дало нам отклонения первого дня d0… dN.
Для макса это абсолютное отклонение dk=|M-sk|, для скво — квадрат отклонения dk=(M-sk)^2 — это член суммы для дисперсии.


Во второй день ещё прочитали — от первого дня до 0..N главы включительно.
s'[i,k] = p[i] +… + p[k-1] — прочитано во второй день
d'[i,k] = delta(M, s'[i,k]) — отклонение второго дня.
d"[i,k] = union(d[i], d'[i,k]) — отклонение за два дня
Для макса — это максимум, для скво — сумма (мы потом должны поделить и искоренить, но не будем торопиться).
Итого за два дня мы получаем новые значения d[k] = min(i) d"[i,k] — наилучшие результаты для каждого количества глав.


Третий день — аналогично второму.
И т.д.


Чуть позже напишу на питончике

задача 3 - решение на питоне
import itertools

# выбор целевой функции - и соответствующих операций.
# delta - считает отклонение
# union - складывает отклонения

if 1:
    # максимальное отклонение
    def delta(M, s):
        return abs(M-s)
    def union(s1, s2):
        return max(s1, s2)
else:
    # дисперсия (и, соответственно, скво - но нам проще считать дисперсию)
    def delta(M, s):
        return (M-s)**2
    def union(s1, s2):
        return (s1 + s2)

def paginate(p, K, verbose=True):
    '''
    разбивает главы на дни
    p - количество страниц в каждой главе
    K - количество дней
    verbose - отладочный вывод
    '''
    assert p
    assert K

    # посчитаем бегущие суммы, чтобы быстро находить сумму страниц меж глав i:j
    # с этого момента нам страницы не нужны (и это мог быть итератор, а не список)
    s = [0] + list(itertools.accumulate(p))
    # sum(p[:j]) = s[j]
    # sum(p[i:j]) = s[j]-s[i]
    def sij(i, j):
        return s[j] - s[i]

    assert len(s) > K

    # количества прочтённых глав, от 0 до N+1
    range_N1 = range(len(s))

    # среднее количество страниц в день
    M = s[-1] / K

    # отклонение за один день, меж глав i:j
    def dij_daily(i,j):
        return delta(M, sij(i,j))

    # отклонения предыдущего дня (инициализировано первым днём)
    d_prev = [dij_daily(0,j) for j in range_N1]
    # наилучшие решения предыдущего дня
    # списки количества страниц за каждый день
    # (инициализировано первым днём - прочли всё по 0:j'ю главу)
    h_prev = [[sij(0,j)] for j in range_N1]

    # итоговое отклонение (предыдущий + текущий дни)
    def dij_total(i,j):
        return union(d_prev[i], dij_daily(i,j))

    if verbose:
        print('day 0:')
        for j in range_N1:
            print(f' {j} : {p[0:j]}={h_prev[j]} @ {d_prev[j]}')
        print()

    # для следующих дней - динамическое программирование
    for day in range(1, K):
        if verbose:
            print(f'day {day}:')

        # новые результаты будут здесь
        d_next = [None for _ in s]
        h_next = [None for _ in s]

        # для каждого итогового количества глав, прочтённых к этому дню
        for j in range_N1:

            # находим лучшее предыдущее количество глав
            i = min(range(j), default=0, key=lambda i:dij_total(i,j))

            # компоненты наших формул (для отладочного вывода)

            # суммы страниц предыдущего дня, этот день, к концу этого дня
            hi, hij = h_prev[i], sij(i,j)
            hj = hi + [hij]

            # отклонение предыдущего дня, этого дня, к концу этого дня
            di, dij, dj = d_prev[i], dij_daily(i,j), dij_total(i,j)

            # записываем результат.
            d_next[j] = dj
            h_next[j] = hj

            if verbose:
                print(f' {i}->{j} : {hi} @ {di:.2f}  + {p[i:j]}={hij} @ {dij:.2f}  =>  {hj} @ {dj:.2f}')

        if verbose:
            print()

        # был день текущий, стал предыдущий
        d_prev, h_prev = d_next, h_next

    # возвращаем последний список страниц - охватывающий все главы.
    return h_prev[-1]
задачки типа первой и второй решал с ребенком в 6м или 7м классе…
Зарегистрируйтесь на Хабре, чтобы оставить комментарий