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

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

    Можно свести ее к integer linear programming и решать какой-то из множества существующих библиотек/солверов. Если числа маленькие, то можно или полный перебор или какую-то динамику, типа решения задачи о рюкзаке сделать (тут надо будет набранные суммы во всех приборах взять в параметры).

    Если нужно не обязательно оптимальное решение - а что-то не слишком ужасное, то можно делать жадность, как Adamos предложил.
    Ответ написан
    Комментировать
  • Как возвести decimal в степень с плавающей точкой?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вы что-то напутали.
    1.000001**2**19 тоже возвращает 1.689255227180379. Только что в консоли проверил.

    > costumePow(1.000001, 19)
    1.689255227180379
    > 1.000001**2**19
    1.689255227180379
    Ответ написан
    6 комментариев
  • Как перебрать все возможные комбинации символов?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Готовой функции нет.

    Нужно или писать рекурсивную функцию, или итеративно дописывать ко всем элементам массива по одному элементу из сделеющего множества. Просто переведите этот код на с++.

    Рекурсивная функция вроде как должна быть более дружественная к аллокациям и по этому - быстрее.
    Ответ написан
    Комментировать
  • Почему моя реализация сортировки слиянием не работает?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    При копировании назад из временного массива b в массив a у вас индексация с ошибкой. Хоть b индексируется с 0, вы обращаетесь к элементам начиная с s.
    Ответ написан
    1 комментарий
  • Как решить данную задачу?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вспомните алгоритм преобразования числа из 10-чной системы в t-ичную - надо делить на t с остатком, пока есть что делить. Остатки - цифры в t-ичной системе счисления (от младших к старшим).

    Так, 323 в десятичной системе, если перевести в 16-ричную будет:
    323 = 16*20+3. Остаток 3, результат 20.
    20 = 16*1+4. Остаток 4, результат 1
    1 = 16*0+1. Остаток 1. результат 0.

    Отсюда получается, что искомое число в 16-ричной системе 143. Проверка 16*16*1+16*4+3 = 256+64+3=323

    Итак, вам надо сделать то же самое, но не в 10-чиной системе, а в k-ичной (ведь переводим не из 10-чной а из k-ичной). Число записано в виде k-ичных цифр в массиве. Это, фактически, длинная арифметика с базой k.

    Для этого надо реализовать деление числа в виде массива цифр на короткое с остатком.
    Это делается со старших разрядов к младшим. Тупо делите каждую цифру с остатком отдельно. Остаток переносите в младший разряд. Последний остаток - это остаток от всего деления. Результаты делений - новые цифры.

    Удобнее хранить цифры от младших к старшим в массиве. Т.е. элемент массива 0 - это единицы. Элемент 1 - это первая степень k. Еще стоит хранить номер последней ненулевой цифры. Смотрите реализацию по ссылке выше.

    Вот пример из условия. Пусть k=666, t=10, число - {2, 179, 113} (в условии цифры от старших к младшим, но мы храним наоборот).

    Делим на 10.

    113/10 = 11 + остаток 3. Переносим 3 в младший разряд (умножив на 666), получаем 3*666+179=2177.
    2177 / 10 = 217 + остаток 7. 7*666+2 = 4664.
    4664 /10 = 466 + остаток 4. Дальше разрядов нет, значит 4 и есть остаток от деления {2, 179, 113} на 10. Результат деления - {466, 217, 11}

    Последний остаток - 4 - это и есть младшая цифра в ответе (он в условии написан - 50241044). Надо продолжать делить результат пока он не окажется 0.

    Повторим для {466, 217, 11}:
    11/10 = 1 + остаток 1. 1*666+217 = 883
    883/10 = 88 + остаток 3. 3*666+446 = 2444
    2444/10 = 244 + остаток 4.
    Опять, последний остаток - это остаток всего деления и следующая цифра в ответе (опять 4).
    Результаты деления - {244, 88, 1}

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

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

    Можно проверить в 2 этапа. Подсчитать A=a*x+b, B=c*x+d. Потом проверить что A*x*x = -B. Но тут нужно заранее отсечь случаи, когда abs(A) > abs(B)/(x*x). Тогда переполнений не будет.

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

    if (d != 0) {
      n = d;
    } else {
      s.push_back(0);
      if (c != 0) {
        n = c;
      } else if (b != 0) {
       n = b;
      } else {
       n = a;
      }
    }
    if (n == 0) {cout << "-1"; return}
    Ответ написан
    2 комментария
  • Как решить задачу с перебором?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Самое простое решение для понимания - полный перебор. Тупо 3 цикла - сколько монет с номиналом 2 взяли, сколько монет с номиналом 3 и сколько монет с номиналом 5. Каждый цикл от 0 до <Сколько осталось набрать> / номинал. Потом проверяем, что сумма сошлась. Более того, можно последний цикл пропустить и просто проверить, что оставшееся можно набрать пятаками.

    bool CanExchange(int n, int have2, int have3, int have5) {
      for(int take2 = 0; take2 <= min(have2, n/2); ++take2) {
        int left2 = n - 2*take2;
        for (int take3 = 0; take3 < min(have3, left2/3); ++take3) {
          int left23 = left2 - take3*3;
          if (left23 % 5 == 0 && left23 / 5 <= have5) {
            return true;
          }
        }
      }
      return false;
    }


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

    Самое быстрое решение - динамическое программирование.

    F(n,k) - можно ли набрать число n первыми k типами монет.

    F(0.0) = 1
    F(*, 0) = 0
    F(n,k) = Sum(i = 0..have[k]) F(n-i*value[k], k-1)

    Фактически, это тот же рекурсивный перебор, только с мемоизацией. Можно переписать это двумя циклами и соптимизировать по памяти до O(n).
    Ответ написан
    Комментировать
  • Что не так в использовании длинной арифметики?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Проблема в выводе. Вы нулевые цифры вообще не выводите:
    for(size_t i=0; i<SIZE; i++) {
      if (n[i]!=0) {
        cout << n[i];
      }
    }


    А нужно пропускать только ведущие нули. Самый простой способ исправить - завести bool printed:
    bool printed = false;
    for(size_t i=0; i<SIZE; i++) {
      if (printed || n[i]!=0) {
        cout << n[i];
        printed = true;
      }
    }


    Ну, еще может быть стоит увеличить размер длинных чисел. Не факт, что 135 цифр хватит. Введите тест n=300, k=300, и проверьте что выводится один и тот же ответ при текущем и увеличенном SIZE.
    Ответ написан
  • В чем может быть ошибка?

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

    Посидите с бумажкой, тут вообще можно формулу для ответа вывести.
    Подсказка: сколько существует i<=j, таких что j-i <= a? Перобразуйте второе неравенство, получите i >= j-a.
    В итоге у вас есть неравенства j-a <= i <= j. Если j >= a+1, то ровно a+1 чисел в этом интервале. Иначе их там j, (потому что левая граница отрицательна или 0, а по условию i >= 1).

    Итого ваш ответ - это сумма от 1 до a, а потом еще n-a слагаемых a. Сумма первой части - арифметическая прогрессия.

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Определитесь со смыслом start и finish. start - это первый элемент интервала, в котором искомый x может быть. finish, судя по тому, что он равен сначала len(lst) - это за концом интервала (не входит в него).

    Значит, исходя из этого, надо цикл гнать пока start < finish - тут правильно.

    При переходе наверх надо start делать mid+1 - тут правильно.

    При переходе вниз надо finish делать mid - тут ошибка! Ведь только что рассмотренный элемент вы выбрасываете, раз он не подошел. Но предыдущий за ним может быть искомым. Нельзя делать finish = mid-1 - вы теряете потенциально важный элемент.

    Другой вариант исправления - изменить смысл finish быть концом отрезка включительно. Тогда присвоения в цикле правильные, но неверно условие цикла и инициализация. Нужно гнать цекл пока start <= finish и изначально присваивать len(lst)-1.
    Ответ написан
  • Как реализовать такой алгоритм?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Можно генерировать поле "с конца". Что в итоге должно получиться? Очень большое число? Вот и начинайте с него. Скажем, 20. Нужно поэксперементировать - чем больше число, тем более плотно заполнено начальное поле будет.

    Берете случайное число на поле, вокруг которого есть 2 свободных клетки. Растраивайте его на три меньших - убираете случайным образом одно из этих трех чисел. Если в результате на поле остается 3 одинаковых числа рядом, то этот ход был невозможным. Проверять достаточно только последние добавленные 2 числа. Запоминайте, какого типа было число - это и будет "случайным" сгенерированным числом.
    Ответ написан
    Комментировать
  • Как решить эту задачу динамическое программирование?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это стандартное упражнение на ДП.

    F(i,j) - максимальное количество монет, которые можно собрать придя в точку (i, j).

    F(0,0) = Z[0,0]
    F(i,j) = Z[i,j] + max(F(i-1,j), F(i,j-1))
    F(-1,*) = F(*,-1) = -100500
    ответ - F(n,m)

    Для восстановления пути сохраняйте в каждой ячейке, с какой стороны взят максимум - сверху или слева. И разворачивайте ответ с конца по этим ссылкам.
    Ответ написан
    1 комментарий
  • Как составить формулу?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    M%p = 0, значит M=k*p для какого-то целого k.

    k*p >= N, значит k >= N/p.

    k - целое, а справа в неравенстве может быть вещественное число. Раз k должно быть больше, то можно вещественное число округлить вверх.

    k >= ceil(N/p).

    слева и справа целые числа, надо найти минимальное k, значит k = ceil(N/p).

    Отсюда весь ответ M = p*ceil(N/p)

    ceil(N/p) можно подсчитать в целых числах в C, как (N+p-1)/p

    Еще, если подумать, что вам нужно блищайшее делящееся на p число, то нужно дополнить N%p до p, то можно сделать так: N+(N%p ? p-N%p : 0)
    Ответ написан
    2 комментария
  • Как провалидировать ход коня?

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

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

    Надо вместо сортировки тупо просуммировать все числа и поделить на их количество (округлите вниз).

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Представьте, что ваши вершины лежат на прямой OX в координатах (a_i, 0). Длины ребер тут будут тупо длинами отрезков. Теперь ваша задача обойти все точки на прямой и вернутся в начало, пройдя наименьшее расстояние.

    Очевидно, что оптимальное решение тут, например, такое: начните в самой левой точке и идите по ним слева-направо. Дойдя до конца, вернитесь в самую левую точку. Это и будет оптимальное решение. Длина пути тут 2*(max(ai)-min(ai)).

    Если нужна только длина пути - то можно тупо найти минимум и максимум и взять удвоенную разность. Если нужен сам путь, то сортируйте или пары {a_i, i} или сортируйте только индексы, используя собственную функцию сравнения, которая по двум целым числам сравнивает a[i1] и a[i2].
    Ответ написан
    1 комментарий
  • Почти любой алгоритм, можно немного изменить, радикально уменьшив время его работы в лучшем случае. Как?

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

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

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

    Но, например, алгоритм суммирования всех чисел в массиве или поиска минимума вы так не ускорите.
    Ответ написан
  • Как максимально оптимизировать код Python?

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

    Есть вот такая формула. Для каждого простого числа можно опредерить степень просто деля n (не факториал!) на это простое число нацело, пока не получится 0 и суммировать все результаты.

    Но это работет только для простых чисел. Т.е. чтобы узнать, в какой степени число 10 входит в N! (или сколько нулей на конце в деситичной записи) надо узнать, сколько раз 2 и 5 входят в N! и взять минимум.

    Т.е. надо разложить k на простые множители, для каждого простого множителя подсчитать, сколько раз он входит в n! по формле выше. Потом поделить это на степень простого числа в k, и по всем делителям k взять минимум.
    Ответ написан
    Комментировать
  • Как разложить полигон на дерево вершин?

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

    В случае с трассировкой лучей, за логарифм нет реализации вообще - ибо у невыпуклого многоугольника может быть O(n) пересечений с лучем (представьте себе пилу).

    В случае с ближайшей точкой - если и есть структура, то это то-то вроде trapezoidal map - это такая мозговыносящая штука, что не советую о ней даже думать. Нужно разбить пространство на области, ближайшие к каждой стороне.

    За логарифм есть решение через бинарный поиск.
    Разбейте пространство на вертикальные колонны всеми x координатами всех вершин многоугольника. Внутри каждой из них будет проходить четное количество сторон. Составьте для каждой стороны уравнение и отсортируйте их по высоте.

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

    Проблема этого метода, что он требует O(n^2) памяти в худшем случае и такую же предобработку.
    Но, для выпуклых многоугольников, прямых в каждой колонне будет всего две. Вот описание метода с реализацией для этого упрощенного случая.
    Ответ написан
    1 комментарий
  • Какой алгоритм для поиска подстрок в списке кортежей будет быстрее comprehension?

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

    Все ваши ключи складываете в бор, как описано по ссылке. Потом для каждого кортежа выполняйте поиск в строке i[1], если нашли, то добавляйте ваш id в ответ.
    Ответ написан
    3 комментария