Ответы пользователя по тегу Алгоритмы
  • Kак показать на экране все числа до заданного числа, которые будут кратны второму заданному числу через цикл (while)?

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

    Это реально 3 строки кода, если вам непонятно, снова почитайте учебник про циклы.
    Ответ написан
    Комментировать
  • Как перевести строку в число в ассемблере?

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

    Надо уметь только делить с остатком и умножать на 10. Как перевести 1234 в строку? Можно взять последнюю цифру - осток от деления на 10. Вот вы получили цифру 4. В строке это будет символ "4", или байт со значением 0x34. Вообще, для получения символа по цифре - надо прибавить 0x30. Это мы взяли остаток, а вот результат деления - 123. Можно продолжить перевод так же и мы получим символы в обратном порядке.

    Итак, пока число не 0, делим нацело на 10. Остаток приписываем в ответ переводя в символ. В конце разворачиваем строку.

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

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

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

    Второй вариант - выходные дни недели подсчитать ручками: если дата начала и дата коца в одной неделе, то выходных нет. Иначе, если начало не понедельник - то сдвинуть ее до следующего понедельника и прибавить следующие субботу и воскресение к счетчику. Потом, если дата конца не пятница - сдвинуть ее на предудущий понедельник. Теперь разность между датами кратна 7 и покрывает сколько-то недель целиком. Делите разность на 7 и умножайте на 2, чтобы подсчитать, сколько там суббот и воскресений (разность может быть и 0 и это нормально).

    Потом надо ручками же подсчитать все праздники. Для этого составьте в массиве даты всех календарных праздников в затронутых годах отсортированные. Потом для каждой задачи в этом массиве бинпоиском найдите первый и последний праздник в отрезке. Вот вы уже знаете их количество (разность индексов +1). Так же можно еще вести список всех рабочих суббот и воскресений. Этот список дат можно, наверно, где-то из интернета автоматически скачивать, а можно и тупо ручками в январе вбивать в базу.
    Ответ написан
    Комментировать
  • Почему один код выполняется быстрее другого C++?

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

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

    Поскольку второй код отбрасывает все копейки при прибавлении, то ему требуется больше итераций, чтобы дойти до y. На самом деле он может даже виснуть, если там получается прибавление 0.
    Ответ написан
    1 комментарий
  • Как с MathNet.Numerics уменьшить число коэффициентов у преобразования Фурье?

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

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

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

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

    Решение со стеком будет линейным.
    Ответ написан
    Комментировать
  • Как получить все точки в промежутке координат XY1;XY2?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Проще "отсортировать" заданные координаты. Если x1 > x2, поменяйте их местами. То же с z1 и z2, А потом остается только первый случай в вашем коде.

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Константа вылезает из деталей реализации операций. Вот сортировка пузырьком делает ровно n(n-1)/2 сравнения и помен в худшем случае. Еще столько же операций инкремента счетчика индекса, столько же проверок на конец цикла. Плюс еще n операций для внешнего цикла. И все это O(n^2). Хотя там есть есть /2 и операций четыре типа. И они еще разное время занимают. Скажем, проверки занимают 10 тактов, а инкрименты - 1. Но вся эта мишура не влияет на скорость роста и прячется в константе. Если все сложить. То будет C*n^2 +C2*n+C3 тактов на алгоритм.
    Ответ написан
    Комментировать
  • Как описать алгоритм для работы с очередями?

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

    Если же числа небольшие, то для двух касс еще есть решение через динамическое программирование, но его сложно обобщить на большее количество касc.
    Ответ написан
    Комментировать
  • Как правильно пропарсить лабиринт в граф?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Самое простое - это сделать вершиной каждую клетку и гнать в этом графе поиск в ширину. Можно даже не строить граф явно - а просто при обходе смотреть на четырех соседей и проверять, а не стена ли там, а были ли мы в клетке (x+dx[k], y+dy[k]), и класть координаты в очередь, если надо.

    Вы же хотите считать только перекрестки и повороты врешинами. В худшем случае, если поворотов в лабиринте много - это будет даже медленнее обхода в ширину. Потому что он работает за линию, а дейкстра - за O(n log n) в лучшем случае. Плюс там константа больше у этого n log n. Правда, это можно решить если вместо дейкстры использовать обобщенный 0-1-BFS (BFS с ограниченными ребрами - там где много очередей используется). Но реализация будет сильно посложнее простого BFS.

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

    Что-то типа такого:
    int last_node = 0;
    for (i = x_start, j = y_start; i <n && j < m; i+= dx, j+=dy) {
       if (is_wall[i][j]) {
        last_node = -1;
      }  
      if (node_number[i][j] <- 0) continue;
      if (last_node > 0) {
        // добавить ребро между вершинами.
        AddEdge(last_node, node_number[i][j]);
      }
      last_node = node_number[i][j];
    }


    Тут я просто добавляю ребра в строке или столбце между двумя соседними вершинами.
    Ответ написан
    Комментировать
  • Алгоритм максимально равномерного распределения предметов?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    std::vector<int> DistributeExtraObjectsFairly(std::vector<int> objects, int extra_objects) {
        std::vector<int> permutation;
        permutation.reserve(objects.size()+1);
        permutation.resize(objects.size());
        std::iota(permutation.begin(), permutation.end(), 0);
        std::sort(permutation.begin(), permutation.end(),
            [&objects](const int &a, const int &b) {return objects[a] < objects[b];});
        int num_increased = 1;
        permutation.push_back(0);
        int step = (objects[permutation[num_increased]] - objects[permutation[num_increased-1]]) * num_increased;
        while (extra_objects >= step && num_increased < objects.size()) {
            extra_objects -= step;
            ++num_increased;
            step = (objects[permutation[num_increased]] - objects[permutation[num_increased-1]]) * num_increased; 
        }
        int final_value = objects[permutation[num_increased-1]] + extra_objects / num_increased;
        int num_bigger_values = extra_objects % num_increased;
        for (int i = 0; i < num_bigger_values; ++i) {
            objects[permutation[i]] = final_value + 1;
        }
        for (int i = num_bigger_values; i < num_increased; ++i) {
            objects[permutation[i]] = final_value;
        }
        return objects;
    }


    Идея: отсортировать числа и добавлять к минимальному, пока оно не станет равно следующему. Потом добавлять к двум мнимальным. Потом к трем и т.д. Вместо добавления к каждому числу индивидуально по одному объекту считаем, сколько надо объектов чтобы первые несколько первых стали равны следующему. Когда нашли, что дальше не приравнять - распределяем остаток среди затронутых элементов по-ровну, остатки кидая куда-то.

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

    Edit: работает за O(n log n), где n - количество элементов в списке. Быстрее, я подозреваю, никак. Есть еще решение за O(n log M) - где М - максимально возможное число во входных данных. Это через бинпоиск по минимальному числу в конце.
    Ответ написан
    Комментировать
  • Путь коня или какова польза ограничений?

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

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

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

    Смотрите на ограничания - n и m до 100000. Обычно такой порядок чисел означает, что вам нужно решение за O(n+m) или что-то вроде O(n+m log (n+m)). У вас же решение "в лоб" за O(nm), что никак не ускорить принципиально. Даже переписыванием на си со всякими хитростями вы ускорите его в 10, ну в 100 раз. А вам надо ускорить его в 10000 раз.

    Могу дать подсказки:
    Во-первых, если и там и там есть положительные элементы, то взяв максимальные в двух массивах вы точно получите элемент больше всех a и b. То же, если и там и там есть отрицательные элементы - надо взять два минимальных.

    Остался случай - что если один массив целиком отрицателен, а второй - целиком положителен. Пусть a положителен, а b отрицателен. Что будет, если взять минимальные по модулю элементы и там и там? Посмотрите внимательно - вам дано, что ни одно из чисел не 0. Это поможет.
    Ответ написан
  • Можете решать эту задачу?

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

    Воспользуйтесь алгоритмом тарьяна: https://e-maxx.ru/algo/lca_linear_offline для получения ответов на все запросы LCA сразу.

    Сначала при вводе создайте дерево целиком и запомните все запросы Get в массив. Потом пройдитесь по массиву запросов и раскидайте их в списки на вершинах, как надо в алгоритме таръяна.

    Вам надо лишь чуть-чуть подкорректировать этот код, чтобы вместе с вершиной-запросом добавлять в списки к вершинам номер этого запроса. И тогда вместо вывода v+1, q[v][i]+1 вы сможете записать ответ в массив по нужному индексу.

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

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

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

    Если вы не нашли делителя до корня, то число простое и в качестве делитеял можно брать все K и тогда ответ будет K-1.
    Ответ написан
    2 комментария
  • На сколько критичен сдвиг в массиве для алгоритмов?

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

    Но самое быстрое решение вашей задачи будет положить один массив в хеш таблицу, и при проходе по второму проверять, есть ли текущий элемент в таблице. Для борьбы с повторяющимеся элементами вв таблице по ключу храните счётчик одинаковых элементов и уменьшайте его при нахождении совпадения.
    Ответ написан
    4 комментария
  • Массовое сравнение сток, поиск пересечений, каким инструментом воспользоваться?

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

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Как выяснили в комментариях, метрика будет sum_i w[i]^2 (actual[i]/ needed[i] - 1) ^ 2.

    пусть x[i] - соклько i-ого продукта
    Пусть a[i][j] - сколько в еденице i-ого продукта j-ого ингредиента. Тогда всего j-ого ингредиента будет sum_i a[i][j] x[i].

    Тогда получаем следующую оптимизационную задачу:

    Sum_j (sum_i A'[i][j] x[i] - C[i]) ^ 2 -> min
    sum_i x_i = 1
    x_i >= 0

    Сумма всего приравнивается к 1, потому что иначе надо считать пропорции и получится вообще ужас-ужас.

    A'[i][j], C[i] - какие-то числа, которые элементарно получаются из a[i][j], w[i] и needed[i].
    A'[i][j] = a[i][j]*w[j]/needed[j]
    C[j] = w[j] / needed[j];

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

    Можно применить метод лагранжа, а точнее метод Каруша — Куна — Таккера

    Сначала выразите x_1 = 1 -sum_j>1 x_j, подставьте во все уравнения и получите:
    Sum_j (sum_i A''[i][j] x[i] - C'[i]) ^ 2 -> min
    sum_i x_i -1 <= 0
    x_i >= 0

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

    Для условия жесткости придется рассмотреть 2 случая - лямбда - 0 или тогда sum_i x_i - 1 = 0

    Прорешать оба случая (СЛАУ) и взять те, где все получается положительным.

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

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

    Можно итеративно - перебирайте число от 0 до 2^|количество чисел|. Биты в этом числе будут обозначать, берется ли конкретный элемент множества в сумму. Дальше считайте сумму текущего подмножества пользуясь бытовыми операциями, чтобы проверить стоит ли заданный бит в 0 или 1. Если набрали половину общей суммы - выводите те индексы, где биты 1 как одно множество, а оставшиеся - как второе. Чтобы каждое разбиение не выводить 2 раза, требуйте, чтобы старший бит был 0 (перебирайте до 2^|количество чисел-1|)

    Можно рекурсивно. Рекурсивная функция принимает текущий набор чисел и номер следующего числа кандидата. Функция или берет или не берет текущего кандидата и запускается рекурсивно передавая следющий номер как кандидата. Если все числа рассмотрены - проверяйте сумму и выводите разбиение. Можно соптимизировать - если уже набрали сумму больше половины, дальше перебирать смысла нет. Опять же, первое число можно потребовать всегда брать или всегда не брать, чтобы не выводить одно и то же разбиение 2 раза.
    Ответ написан
    Комментировать