• Какой генератор алгоритмов на основе входных и выходных данных вы сейчас используете?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    В общем виде, когда надо найти функцию любого вида (предположительно самую короткую) - задача не решается. Тут нужен искусственный интеллект. Настоящий, а не машин лёрнинг.

    Но, если ограничить класс допустимых формул, то решение есть - например, среди полиномов для n заданных эталонов можно всегда найти полином степени n-1, который будет через эти точки проходить. Это если у вас входные и выходные данные - по одному числу.

    Тут можно решить систему из n линейных уравнений (обозначаете неизвестными коэффициенты полинома, подставляете известные значения x и y для всех эталонов, гоните метод Гаусса).

    Если у вас несколько выходных чисел - решаете задачу интерполяции отдельно для каждого выходного параметра. Если несколько входных переменных - тоже выбирайте вид полинома, зависящего от всех входных переменных, имеющего хотя бы n коэффициентов. Но тут уравнения могут стать линейно зависимыми и решения может не быть. Тогда добавьте больше слагаемых/коэффициентов.

    Формально, задача будет решена, но практического смысла в этом нет совсем. Формулы будут огромными и страшными.
    Ответ написан
  • Наиболее эффективный алгоритм для бронирования?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это же в чистом виде задача о рюкзаке (вес - это вместимость номера, в человеках, а цена - стоимость). Тут надо набрать номеров ровно заданной вместимости с наименьшей стоимостью (в задаче вообще без разницы, максимизировать или минимизировать общую цену).

    Если у нас есть n людей, то заводим массив от 0 до n. Помечаем, что 0 человек можно размесить за 0 рублей, а все остальное за бесконечность. Потом для каждого свободного номера ()проходиммассив с конца в начало и, если (текущаяя цена + цена номера) для k человек лучще чем цена размещения (k+вместимость номера), то перезаписываем лучшую цену для k+вместимость. Так же сохраняем в вспомагательном массиве, какой номер мы сейчас взяли для данного количества человек (k+вместимость номера).

    В конце смотрим на ячейку для n человек - там минимальная стоимость будет.

    В итоге, время работы алгоритма O(n*m), где n человек и m свободных номеров.

    Это решение динамическим программированием даже короче и легче полного перебора: буквально 2 вложенных цикла для расчета и один while для восстановления ответа.
    Ответ написан
  • Как использовать транспортную сеть оптимально?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Не могу сказать, насколько это решение будет оптимальным по времени, не зная предполагаемого размера графа. Но есть решение через максимальный поток, котрое точно наилучшим способом пустит машины.

    Раздуйте граф, сделав копии каждой вершины для каждого возможного времени. Т.е.если предполагается, что есть решение не длинее 1000 едениц веремни, то создаете граф с 1000*V вершинами, по одной для каждой вершины начального графа и возможного времени. Для каждого ребра входного графа u->v создайте ребро {u,t}->{u,t+1}. В этом графе есть много входных вершин (любое время, начальная вершина) и много конечных вершин (любое время). Но тут уже нет условия на непересечение машин в одно и то же время. Вместо этого пути машинок просто не могут пересекаться по вершинам вообще. Ведь каждая вершина символизирует вершину+время.

    Теперь еще раз преобразуем граф - сделайте новую входную вершину и соедените ее со всеми входными вершинами в этом графе. Также сделайте новую конечную вершину и соедените ее со всеми конечными вершинами. Каждую оставшуюся вершину разделите на 2 - вход и выход. Все ребра ведущие в эту вершину пустите во вход, и так же поведите все ребра из начальной вершины из выхода. Соедените вход и выход ребром.

    В этом графе пути уже должны не пересекаться по ребрам (ведь каждая вершина заменена ребром между двумя новыми вершинами) и все пути ведут из начала в конец. Чтобы разрешить машинам пересекаться в начальной и конечной вершине, начальные и конечные вершины графа не раздваивайте и сделайте пропускную способность ребер из начальной и в конечную вершины равными n. Все остальные пропускные способности равны 1.

    Теперь пустите максимальный поток в этом шрафе, и он найдет вам сколько-то путей машин не пересекающихся по ребрам. Эти пути однозначно задают вам пути машин в изначальном графе - когда выпускать машину и по какому пути она идет.

    Что бы найти оптимальный пути запустите бинарный поиск по ответу. Вот выбрали вы число 1000, создали искуственный граф со временем до 1000 для всех вершин. Запустили в нем максимальный поток. Если он нашел меньше n путей, то за 1000 едениц времени все n машин не пустить, пробуйте большее время. Если нашли хотя бы n путей, то можно взять любые n из них.

    Изначальную верхнюю границу по времени можно взять n+V (V - путь в графе, и все машины идут по нему колонной одна за другой).

    Возможно есть улучшение этого решения такое: Вместо бинарного поиска по ответу вы увеличиваете максимальное время на 1, добавляете новые вершины и ребра в граф и каждый раз ищете дополняющие пути (не отчищая уже найденый максимальный поток). Это рещение вроде будет побыстрее, но тут надо аккуратно понимать, что такое остаточная сеть.
    Ответ написан
    1 комментарий
  • Правильный ли алгоритм?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Очень похоже на переполнение. Попробуйте тест, где одно устройство и миллион задач по 100 каждая.
    Ответ написан
    Комментировать
  • Как найти кратчайший маршрут в графе с дополнительными требованиями к маршруту?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    При данных ограничениях можно создать искусственный граф, где каждая вершина соответствует вершине в исходном графе и некоторому запасу топлива (я обозначу такую вершину как (v, f) - в вершине v с топливом f).
    Вершин в таком графе будет до 500*10000. Для каждого ребра v->u длинной l в исходном графе создаем ребро из вершины (v, f) в вершину (u, f-l) для всех f (т.е. из вершины v, имея f топлива, можно попасть в вершину u, имея f-l топлива). Эти ребра ценой 0. Также, в каждую вершину v, где есть заправка, добавьте ребро (v, f) -> (v, n) ценой 1 (заправились до упора). В графе будет до 500*10000 + 500*10000 ребер.

    Теперь запустите там обход в ширину с двумя очередями или deque из вершины (start, n) пока не найдете путь в любую вершину (finish, f). Длина пути будет равна количеству заправок. Преходы по ребрам ценой 1 - это процесс заправки, по ребрам цены 0 - просто передвижения в исходном графе.

    Это известное обобщение обхода в ширину на 0-1 графе:
    Когда берете вершину из deque и смотрите всех ее соседей, если длина ребра до соседа 1, то кладете конец ребра в конец deque, а если цена 0 - то в начало. С двумя очередями делается так: пока очереди не пусты, берем вершину из текущей очереди и кладем ее соседей на расстоянии 0 в конец той же очереди, а соседей через ребро длины 1 - во вторую очередь. Когда текущая очередь опустеет, переходим к другой и так далее.
    Ответ написан
    4 комментария
  • Фибоначчи: Возможно ли найти порядковый номер последовательности Фибоначчи ( x ), исходя из числа ( y )?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Не школьник, но вроде все просто.
    У вас есть y = B^x/A
    Дано y, отсюда x= log_B(A*y) = ln(A*y) / ln(B).

    Надо будет округлить. Формула может давать сбои на мелких числах.
    Ответ написан
    1 комментарий
  • Как посчитать кол-во возможных исходов?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Ответ - количество сочетаний по n-1 из (n+m-2), или сколько способов выбрать n-1 объект из n+m-2.

    Это потому что нужно обязательно сделать n-1 шагов вниз и m-1 шагов вправо. Вопрос только, в каком порядке их делать. Всего будет сделано n-1+m-1 шагов и из них надо выбрать какие-то n-1 вниз, остальные будут шаги вправо. Вот и получаются сочетания.

    Можно считать треугольником Паскаля, получится в точности то же, что описал poznavaka,
    можно считать по формуле факториалов: (n+m-2)! / (n-1)! / (m-1)!

    Для вашего примера, где N=3 и M=4 ответ будет 5!/2!/3! = 120/2/6 = 10.
    Ответ написан
    Комментировать
  • Можно ли единожды обойти все вершины связного неориентированного графа и вернуться в начальную вершину?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Судя по описанию, вам нужен Гамильтонов путь.

    Эта задача NP-полна - следовательно, простого и быстрого алгоритма человечеству не известно. Можно делать полный перебор с отсечениями и эвристиками. Какие-нибудь методы имитации отжига или генетические алгоритмы могут работать быстрее, но это не точно и нужно долго и мучительно ковыряться, что бы оно заработало.
    Ответ написан
    Комментировать
  • Python: Сколькими способами можно представить заданное натуральное число N в виде суммы после- довательных нечетных натуральных чисел?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Сумма последовательных нечетных чисел - разность двух квадратов:

    1 + 3 + 5 + 7 = 16 - 0
    7 + 9 = 25 - 9

    Таким образом - вопрос в том, сколько различных пар квадратов дают разность в заданное N.

    N = a^2 - b^2 = (a-b)(a+b)

    Или же сколько чисел a и b, т.ч. N раскладывается на множители (a+b) и (a-b).

    Достаточно найти все делители N (не больше корня) т.ч. делитель и оставшаяся часть имеют четную разность (эта разность же равна 2b). Т.е. (n/d - d) %2 = 0.

    Перебирайте все числа d до корня N, и если N%d == 0 and (N/d -d) %2 == 0, то прибавляйте единицу к ответу.
    Ответ написан
    Комментировать
  • Какой оптимальный алгоритм нахождения суммы Минковского?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Для двух невыпуклых многоугольников с n и m вершинами сложность вычесления (nm)^2. Для 300 вершин - это уже порядка нескольких секунд. Можно сильно напрячься и ускорить на несколько процентов. Но сильно быстрее вы ничего не найдете.
    Ответ написан
    Комментировать
  • Как найти последную ненулевую цифру K!?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Можно быстро, за log (k) подсчитать, сколько там нулей в k!

    Для этого какая там степерь 2 и 5 и возьмите минимум.
    Простое p войдет в k! в степени floor(k/p) + floor(k/p^2) + floor(l/p^3)...
    Можно легко это реализовать рекурсивно PowerInFactorial(n, p) = n >= p ? n/p + PowerInFactorial(n/p) : 0;

    Теперь вы знаете, сколько нулей в факториале. Если их все убрать, то задача становится совсем простой - найти последнюю цифру. Это можно сделать просто ведя все вычисления по модулю 10.

    Т.е. перемножаете все числа от 2 до k, предворительно сокращая двойки и пятерки, сколько надо.

    twos = fives = min(PowerInFactorial(k, 2), PowerInFactorial(k, 5));
    ans = 1;
    for ( i = 2; i <= k; i++) {
      j = i;
      while (j % 2 == 0 && twos > 0) {
        j/=2;
        twos--;
      }
      // same for fives
      ...
      ...
      ans = (ans*j)%10;
    }


    В итоге решение за O(k), потому что вложенные циклы вполнятся суммарно столько раз, сколько нулей в k!, а их по формуле выше, меньше O(k).
    Ответ написан
    Комментировать
  • Правильно ли я понял алгоритм быстрой сортировки?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Такая форма получения среднего в диапазоне от L до R берется, чтобы не было переполнения.

    L+(R-L)/2 = L-(L-R)/2 = (L+R)/2. Да тут в зависимости от четностей может быть разница +-1, но это не важно. Но в последнем выражении L+R может переполнится, если L и R большие, хотя результат всегда помещается в int.
    Ответ написан
    3 комментария
  • Какие существуют алгоритмы поиска равноудаленных прямоуголиников?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Ваш алгоритм надо лишь немного доработать.

    1) найти ближайший сверху (слева/снизу/справа) из тех, которые имеют перекрытие.
    2) повторить операцию, найдя следующий сверху. Расстояние между ними взять за d.
    3) продолжить идти вверх. Если следующий ближайший тоже на расстоянии d - нарисовать промежуток. Если нет, то остановится.
    4) Зная ближайший прямоугольник из пункта 1) и расстояние d - можно его отступить вниз и у вас есть точка примагничивания.

    Можно ускорить алгоритм немного. Сначала за один проход выделите все прямоугольники, которые выше данного, но пересекаются с ним по оси X. Отсортируйте их снизу вверх по нижней границе. Теперь в пунктах 1,2,3 надо рассматривать только один следующий прямоугольник из списка.

    Если есть пересечения то можно или выбрасывать прямоугольники, пересекающиеся с текущим, или остановится, как будто следующего прямоугольника нет.

    Что бы ускорить еще больше, когда у вас очень много прямоугольников на экране, можно хранить их в quad tree и из него вытаскивать прямоугольники, которые перекрываются с заданным и выше его уже в правильном порядке. Но скорее всего просто пройтись по списку всех прямоугольников будет достаточно.
    Ответ написан
    Комментировать
  • Как будет выглядеть алгоритм?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Решение через динамическое программирование. F(k) - лучшая сумма, которую можно набрать из карточек, начиная с k, когда можно один раз взять 2 карточки, g(k) - тоже самое, но 2 карточки брать уже нельзя.

    g(k) = max(a[k]+g(k+2),g(k+1)); - или вы берете первую карточку, то робот берет следующую и далее задача повторяется. Второй вариант - вы пропускаете эту карточку, тогда ее берет робот. Задача повторяется со следующей карточки.

    f(k) = max(a[k]+a[k+1]+g(k+3), a[k] + f(k+2), f(k+1));
    - так же, мы либо берем 2 карточки, либо 1, либо 0. Если мы берем 2, то в будущем 2 уже брать нельзя, придется брать g(k+3).

    Вот решение на C++ (вместо 2 функций используется флаг, можно ли брать 2 карточки)

    vector<int> dp[2]; // заполнен MIN_INT, размером с n.
    vector<int> take[2]; // размером с n.
    int f(const vector<int> &a, int k, int can_take_two) {
       if(k >= n) return 0;
       if (dp[k][can_take_two] > MIN_INT) return dp[k][can_take_two];
       // skip;
       int best = f(a, k+1, can_take_two);
       take[k][can_take_two] = 0;
       // take 1;
       int cur = f(a, k+2, can_take_two) + a[k];
       if (cur > best) {
         best = cur;
         take[k][can_take_two] = 1;
       }
       if (can_take_two && k + 1 < n) {
          cur = f(k+3, 0) + a[k] + a[k+1];
          if (cur > best) {
            best = cur;
            take[k][can_take_two] = 2;
          }
       }
       dp[k][can_take_two] = best;
       return best;
    }
    //...
    // f(a, 0, 1) - вернет лучшую сумму.
    
    //Для восстановления ответа можно воспользоваться массивом take, который хранит для каждого состояния, сколько карточек брать:
    
    k = 0; ct = 1;
    while (k <n) {
      num = take[k][ct];
      for (j = 0; j < num; ++j) {
        cout << a[k+j] << " ";
      }
      if (num == 2) ct = 0;
      k += num + 1;
    }


    Если нужна только сумма, а не сам набор чисел, то можно развернуть рекурсию в 2 вложенных цикла (один от n до 0, другой от 0 до 1) и хранить только 3 последних строчки массива dp.
    Ответ написан
  • Как отсортировать двусвязный список SplDoublyLinkedList?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Немного через одно место получается, но раз уж вам надо на php, до еще и с SplDoublyLinkedList, то...

    Вы можете вместо одной операции разбиения списка просто в цикле откусывать элемент с конца, с помощью pop и добавлять в другой список с помощью push.

    На n log n сложность это не повлияет. Просто замедлит ровно в 1.5-2 раза, в зависимости от реализации merge.
    Ответ написан
  • Обрезка невыпуклого полигона. Как определить множество полигонов на выходе?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Эта программа не будет работать на таких многоугольниках. Там есть комментарий:

    /* this works only if all of the following are true:
    ...
    * 4. poly is convex (implying 3).

    Приведенный код работает только для выпуклых многоугольников, тут даже проблема не в том, что обрезанная часть несвязная. Возможны более простые случаи, где программа выдаст неправильный ответ.

    Вижу такое решение пробемы - пересечь все ребра со всеми ребрами и поделить ребра точнками пересечения на несколько новых. Пометить ребра в порядке обхода полигонов по часовой стрелке. Затем выполнить алгорим выделения замкнутых областей (описан ниже). Потом в каждую область отдельно проверить, является ли она пересечением обоих полигонов (В этом случае все ребра будут в порядке обхода по часовой стрелке).

    Алгорим выделения замкнутых областей:

    Строим ориентированный граф, где вершины - вершины полигонов (+ точки пересечения), а ребра - направленные отрезки (вместо каждого отрезка создаем два ребра в обе стороны между вершинами - концами отрезка).

    В каждой вершине сортируем все исходящие ребра по углу по часовой стрелке. Так же для каждого ребра при построении надо запонить обратное ребро.

    Затем выполняем обход - От любого пока не пройденного ребра переходим в следующее ребро, идущее как можно левее. Для этого берем обратное ребро и от него следующее в порядке сортировки в вершине. Грубо говоря, ведем пальцем по ребрам каждый раз оставляя текущую область слева от ребра. В таком обходе мы обойдем все ребра замкнутой области и вернемся к начальному ребру. Запускаясь и дальше от всех необойденных ребер мы можем выделить все области.

    Будет одна лишняя внешнаяя область, но там порядок обхода будет другой и она по площади максимальная. Ее можно исключить, посчитав, например, площади всех областей. Или считать со знаком и у нее знак не тот, или по модулю, тогда она - самая большая и сумма всех остальных.

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

    Это не работает, если полигоны не пересекаются. Тогда надо проверить любую точку каждого, содержися ли она целиком во втором полигоне.
    Ответ написан
    2 комментария
  • Как реализовать алгоритм комбинаторики в программе?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Сначала переведите картинку из залачи в числа. Вот есть у вас 20 точек, пронумерованные от 1 до 20, как-то. Выпишите все 12 групп (9 окружностей и 3 диаметра). Будет у вас 12 наборов по 5-6 номеров. Запишите их в программе константными массивами.

    Затем делайте перебор рекурсией: У вас в глобальном массиве или списком в параметре будет набор пока не поставленных чисел. Сначала проверьте, что вы не сделали уже 2 группы с разной суммой. Пройдитесь по всем 9+3 группам, если какая-то целиком уже заполнена, то записываете ее сумму в переменную для окружности или диаметра, соответственно. Если перед записью там уже была другая сумма -то вы нашили противоречие. В этом случае надо просто вернуться из текущей рекурсии назад.

    Если противоречий нет и вы все 20 чисел поставили - выводите ответ.

    Иначе - перебираете, какое число ставите в первую незаполненую точку циклом. Ставите туда число и вызываетесь рекурсивно.

    Надо только аккуратно уже поставленные на картинке числа в нужные места поставить. Для этого их, во первых, не включйте в список непоставленных чисел, во вторых на заполенные позиции ставьте уже записанное там число.

    Просто перебирать все перестановки (наборы массивов из чисел без повторений) и в конце проверять суммы, как вы хотите сделать - будет слишком долго. Их 21! (факториал) - это дофига слишком.
    Ответ написан
  • Как проверить массив на возрастание?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Ваше решение не работает на примере 3,4,1,2. Тут нельзя удалить одно число, но ваша программа вернет true.

    В случае, если вы нашли один участок с убывающими соседними числами, надо проверить, что будет если удалить первое или второе число.
    Ответ написан
    Комментировать
  • Очень быстрый алгоритм умножения длинных чисел, куда копать?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    При умножении на маленькое число всякие хитрые алгоритмы типа преобразования Фурье или Карацубы лаже медленнее тупого умножения в лоб: Просто проходитесь по большому числу от младших разрядов к старшим, умножете на маленький множитель, прибавляете перенос. Потом берете остаток от деления на базу (если множитель маленький, то быстрее будет просто вычесть несколько раз в цикле вместо модуля), а результат целочисленного деления записываете в перенос.
    Ответ написан
    Комментировать
  • Как уравнять графы?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Я так понял, что ребра задают разницу между "высотами" вершин. "получается, что в сумме не равно 0" означает, что конфликтов нет и можно назначить вершинам какие-то высоты, чтобы все ребра были правильными. Я правильно понял условие?

    Тогда задача решается простым обходом в глубину или в ширину, как вам угодно. Просто зафиксируйте одну вершину на высоте 0, и проходя по всем ребрам фиксируйте высоты всех остальных вершин. Если ребро ведет уже к зафиксированной вершине, то можно проверить, что высота остается такой же, но в эту вершину, в любом случае, обход больше не заходит.

    В конце, если вам надо, чтобы все вершины имели неотрицаиельную высоту, например, надо найти вершину с минимальной высотой, и если она отрицательна, вычесть ее из высот всех вершин.

    Обход надо вызывать в цикле, чтобы обошелся весь граф, даже если он несвязный.
    Ответ написан