Задать вопрос
  • Что я не правильно делаю в тестовом задании?

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это обобщение известной задачи, где пропущено одно число. Там можно, например, вычесть все числа из n(n+1)/2. Или про-xor-ить все числа от 1 до n и все числа в массиве.

    Для двух чисел нужно составить какие-то 2 уравнения, Например, найдите сумму двух чисел и сумму их квадратов: Сумма пропущенных чисел - это n*(n+1)/2 - (сумма всех чисел в массиве). Здесь надо аккуратно не забыть о возможном переполнении (если ваш язык, например, С++). Сумма квадратов: сумма квадратов всех чисел от 1 до n минус квадраты всех чисел в массиве.

    Итак, вы за O(n) двумя не вложенными циклами (один для суммы квадратов 1..n, другой для обработки всех чисел массива) нашли X+Y=A и X^2 + Y^2=B.

    Теперь решите уравнения относительно X и Y: Выразите X через Y из первого уравнения и подставьте во второе. Решите квадратное уравнение и получите:
    X = (A-sqrt(2B-A^2))/2, Y = (A+sqrt(2B-A^2))/2.

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

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

    Edit:

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

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если я все правильно понял, то у вас всего 5 кубиков и 3 варианта, на каком ходу зафиксировать кубик. Т.е. всего вариантов 3^5 = 243. Это очень мало - их можно все перебрать. Для этого надо или 5 вложенных циклов, или рекурсивная функция, которой передаются уже собранные числа в массиве, какой следующий кубик перебирать. Если еще не все 5 кубиков собраны, то функция гонит цикл из трех вариантов и для каждого дописывает текущий кубик к массиву, рекурсивно запускается, и удаляет последний кубик, чтобы откатить изменения для следующего варианта. Если все 5 кубиков выбраны, то текущий вариант оценивается и, если надо сохраняется.

    Тут советую написать отдельную функцию проверки, которая копирует массив (или принимает его по значению), сортирует его и тупо проверяет все варианты (Стрит - arr[0] == 1 && arr[1] == 2 ... && arr[4] = 6, пять одинаковых - все числа равыны. 2+3 - первое==второму, третье==пятому или первое==третьему, четвертое == пятому).
    Ответ написан
    2 комментария
  • Как преобразовать слова?

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

    Решается так:
    1) Проверить, что строки равны. Это особый случай
    2) Проверить, что длина одинаковая?
    3) Завести мап символ->символ, пройтись по строкам параллельно и записывать мап[строка1[i]] = строка2[i], если там пусто. Иначе - проверить, что там уже записано то же самое.
    4) Проверить, что различных символов во второй строке меньше 33. Можно с помощью сета, который наполняется в том же цикле, что и в шаге 3.
    Ответ написан
    5 комментариев
  • C#. Есть два массива, как сделать проверку данных на совпадение?

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

    Вариант по-умнее: Отсортировать оба массива, пройтись по ним двумя указателями параллельно (Если текущие элементы совпали - нашли совпадение и двигаем оба указателья, иначе двигаем указатиль на меньший элемент. Если один из массивов кончился - все).

    Лучший вариант: Пройтись по меньшему массиву и сложить его элементы в HashSet<> (через Add()). Потом пройтись по второму массиву и каждый элемент проверить через метод Contains() у сета.
    Ответ написан
    2 комментария
  • Как разделить все точки на плоскости на пары с минимальным расстоянием?

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

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

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


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

    Пример
    3 вершины (0, 0), (1, 0) и (0, 1).
    Матрица расстояний (с 1e90 на диаганали вместо 0):
    ((1e90, 1, 1),
    (1, 1e90, 1.414),
    (1, 1.414, 1e90)).

    Решение ценой 3.414 (3-ий элемент в 1-ой строке, 1-ый во 2-ой строке и 2-ой в 3-ей)
    ((*, *, 1),
    (1, *, * ),
    (*, 1.414, *)).

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Невозможно решить вашу задачу. У вас 65535*255 вариантов входных данных и по условию они должны выдавать разные значения. Итоговое количество чуть не дотягивает до 2^24 - а значит в 16 бит его никак не засунуть.
    Ответ написан
    Комментировать
  • Какова реальная сложность данного алгоритма?

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

    Еще лучший вариант - вместо списка использовать какой-либо set в ids. Это будет работать совсем быстро, если ids длинный, а data короткий.
    Ответ написан
  • Как реализовать графические (мб логические) объекты, вложенные друг в друга, (окна в окнах (как матрешки))? Python? C++? ??

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

    При рисовании новой рамки рекурсивно спускайтесь по этому дереву объектов, пока не получите тот элемент, в котором новая рамка целиком содержится.

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

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

    Как делать прорисовку? Опять же рекурсивный проход по дереву отсекает все элементы, которые не пересекаются с видимой областью. Те, которые пересекаются - сохраняются в какой-то список на прорисовку.

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Один момент меня смущает: зачем вы внутри DFS не вызываете cycle когда у вас есть ребро в p? Это цикл длины 2 - вполне нормальный цикл. Или по условию задачи такие надо исключить?

    А главная ошибка - надо при выходе из dfs помечать вершину другим цветом - как обработанную (например, 2). Чтобы color == 1 только у вершин, которые в стеке. При этом в самом Dfs нужно рекурсивно вызываться только отвершин с color == 0.

    Допустим, у вас в графе есть ребро 2->1 и все. Вы вызоветесь от вершины 1 в цикле в main, ничего не найдете. Потом вызоветесь от вершины 2, найдете ребро в уже обработанную вершину и среагируете на это, как на цикл. Хотя это не цикл.
    Ответ написан
  • Как с помощью регулярного выражения определить регистр?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Подсказка: все большие буквы подпадают под "[A-Z]" (надеюсь, вопрос не про русский язык?). Теперь вам надо проверить, есть ли в строке хоть одна буква подпадающая под эту регулярку.
    Ответ написан
    3 комментария
  • Тестер алгоритма?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Эм... Вам надо читать данные из входного файла (в задаче это input.txt или stdin).

    import sys
    
    n = int(sys.stdin.readline().strip())
    comp = [sys.stdin.readline().strip().split(" "), sys.stdin.readline().strip().split(" ")]


    Это не самый быстрый способ читать в питоне, но это мешает, когда чисел порядка 100000. У вас же в задаче всего ~2000 чисел и можно и так делать.

    Примечание, тут водные числа будут храниться в виде строк. Если вам в задаче нужно именно числа читать, то надо дополнительно map-ом применить int() к элементам.
    Ответ написан
    1 комментарий
  • Задачи с минимальными и максимальным вариантами и неравномерными выборками?

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

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вы правильно решили отсортировать концы отрезков, но эвристика с +-1 в виде второго параметра сортировки у вас не работает. Похоже там и есть ошибка. Попробуйте тест, "6 100 102 100 102 100 102 102 104 102 104 102 104". Ответ должен быть - 1 отрезок длиной 1 (там 6 вакансий в 102-ой секунде). Другой интересный тест "6 100 102 100 102 100 102 103 104 103 104 103 104". Тут ответ 1 5 (3 вакансии).

    Проблема в том что такой подход с сортировкой работает, если границы отрезка - точки на прямой. У вас же в задаче границы отрезка - это секунды.
    Я бы прибавлял 1 к концам отрезков и считал границы точками на числовой прямой.

    Далее, я бы разделил код выделения отрезков и поиска максимума.
    У вас очень сложная логика и там может быть ошибка. Сложно за всем уследить.
    По памяти у вас вроде бы проблем нет - ну заведите еще один массив, куда будете складывать {количество вакансий, длина отрезка}. Складывайте туда только отрезки с разным количеством вакансий. Потом (или во время складывания) найдите там макимальное количество вакансий. И последним проходом подсчитайте сумму и количество элементов в с заданным количеством вакансий.

    Что-то вроде этого (код на недо-жаве, поправьте сами):

    // class Segment: имеет length и count.
    // result - arrayList of Segment
    // arrayList содержит MyClass отсортированные по X границы отрезков (Y= +1 для начала и -1 для конца. Концы отрезков имеют сдвинутую на +1 X).
    
    prev_x = arrayList[0].GetX();
    cnt = 0;
    for (MyClass mc : arrayList)  {
      cur_length = mc.GetX() - prev_x;
      // рассматриваем отрезок (prev_x, mc.GetX()), не включая границы-точки!
      // Поэтому GetY() прибавляем в конце!
      // пропускаем отрезки нулевой длины - они из-за совпадающих точек и смысла не несут
      if (cur_length > 0) {
       //  новый отрезок добавляем, только если разное количество вакансий.
       if (result.size() == 0 || result[result.size() - 1].cnt != cnt) {
        result.add(new Segment(cur_length, cnt);
       } else {
          result[result.size()-1].length += cur_length;
       }
       cnt += mc.GetY();
       prev_x = mc.GetX();
    }
    max = 0;
    for (Segment s : result) {
      if (s.cnt > max) max = s.cnt;
    }
    length = 0;
    total = 0;
    for (Segment s : result) {
      if (s.cnt == max) {
        length += s.length;
        total += 1;
      }
    }
    
    // print total length.
    Ответ написан
  • Как делить выражение на выражение?

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

    x^5 + 0x^4 + 0x^3 + 0x^2 + 0x - 1  | x^3 - 1
    x^5 + 0   +  0    - x^2           x^2
    0    0      0     +x^2  +  0 - 1


    Отсюда (x^5-1)/ (x^3 - 1) = x^2 + (x^2-1) / (x^3 - 1).
    Ответ написан
  • Нужны ли алгоритмы с графами в региональном этапе по программированию?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Да, могут быть нужны. Про то, что ограничения порядка 10^5 - это не факт. Может быть и задача, где ограничения меньше, а решение сложнее.

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

    Далее, что за дерево отрезков на графе?! А еще дейкстра может быть написан за (n+m)logn. И форд-беллман работает за nm, а не n^2.
    Ответ написан
    4 комментария
  • Как вычисляется временная сложность алгоритма, если в алгоритме две сложности?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    На пальцах - можно и складывать и брать большее. Но последнее - более точная оценка. Но на самом деле надо формально расписать по определению. Что значит первая часть O(N)? Для некоторых чисел N1 и С1, при N>N1 первая часть алгоритма делает менее С1*N операций. Аналогично, при N>N2, вторая часть выполняет менее С2*log(N) операций.

    Итого имеем, при N>max(N1,N2), всего выполняется менее C1*N+C2*logN операций. Если взять C = max(C1, C2), то можно сверху ограничить количество операций C*(N+log N). Т.е. мы доказали, что ваш алгоритм - O(N+log N). Далее, начиная с какого-то N>N3 N > log(N). Т.е. при N> max(N1,N2,N3) можно ограничить время работы как C*(N+N) = 2*C*N = C'*N. А это уже означает, что весь алгоритм - O(N).

    Из этих рассуждений должно быть понятно, что при сумме константного количество шагов можно брать O() для самого тяжелого шага. Но, если количество шагов зависит от N, то так делать нельзя, потому что в последнем шаге вылезет не константный множитель (как 2 выше), а что-то зависящее от N.
    Ответ написан
  • Чем отличается такой код?

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

    Во первом варианте, ИМХО, более выразительно видно, какие есть 2 варианта возвращаемых значения и что они выбираются по условию. Такой код мне нравится чуть больше. Однако, если помимо return в else-ветке есть много кода, то уже лишний уровень отступа будет уменьшать читаемость. В этом случае второй вариант кода будет предпочтительнее. Но это все вкусовщина и личное мнение.
    Ответ написан
    Комментировать
  • Как найти кратчайший путь по графу с waypoint-ми?

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

    Тут нет простого и быстрого решения задачи. Есть полный перебор: перебирайте все перестановки (из n-1 вершины) и считайте длину пути в таком порядке. Перебор работает очень медленно (O(n*n!)) и неприменим при больших n. Если у вас, скажем, 20 вершин - вы уже ответа не дождетесь. Для 5, как в примере будет работать моментально.

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