• Как найти угол между средней линией и катетом в треугольнике?

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

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

    Чаще всего используют именно арктангенс, потому что, в отличии от косинусов/синусов, он хорошо работает для любого угла (у арксинуса, например, невозможно различить 45 и 135 градусов - значение синуса одно и то же). У арксинуса и арккосинуса нужно добавлять какие-то проверки сверху для решения неоднозначностей.

    Почему в задаче такой ответ? atan() передаются косинус и синус угла, возможно растянутые на один и тот же коэффициент. Координаты точки М - (1/2 AB, 1/2 BC), ведь это середина AC. Представьте перпендикуляр из точки М на ость OX(BC). В получившемся треугольнике будет прямой угол, гипотенуза BM и катеты с длинами 1/2 AB, 1/2 BC. Соответственно, косинус/синус искомого угла будут 1/2/AM*AB, 1/2/AM*BC. Теперь вспоминаем, что atan() можно передавать значения, умноженные на константу. Вот пусть константа будет 2/AM. Тогда остаются AB и BC.

    Еще, можно смотреть на atan() так - передайте ему координаты конца вектора и он вернет угол между OX и вектором.
    Ответ написан
    1 комментарий
  • Как в диапазоне дат посчитать рабочее кол-во времени?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если не нужны праздники, высокосные секунды и вот это вот все, То я бы делал так:
    1) Сначала привести времена начала и конца к одному и тому же времени суток.
    Отмотайте время конца до полночи назад - прибавьте пересечение отрезка [0, end_time] c [7:00, 22:45] к ответу (если дата конца рабочая). Также отмотайте время начала вперед до полночи, также учитывая работчее время (если дата начала рабочая.
    При этом к дате начала прибавьте 1, а от даты конца 1 вычтите. Отдельно рассомтрите случай, если даты начала и конца одинаковые.

    Тут нужно будет по дате уметь определять рабочая ли она (получать день недели стандартной функцией).

    2) Теперь осталось подсчитать, сколько рабочих дней между двумя датами (включая их). Я бы тупо вычел времена полдней этих дат друг из друга и поделил на 60*60*24 секунд, чтобы узнать сколько суток прошло. Теперь еще надо определить дни недели начала и конца.

    Далее, также как с временем, отматываем дату начала до следующего понедельника, а дату начала до предыдущего воскресенья. Прибавляем к ответу (рабочее время в сутках)*(max(6-номер_дня_недели_начала, 0)+min(номер_дня_недели_конца, 5)). Теперь считаем сколько осталось полных недель (вычитаем сдвинутые даты друг из друга в днях и делим на 7) и прибавляем столько полных рабочих недель. Особый случай, если начало и конец в одной неделе (разница между ними <= 7-номер_дня_недели_начала).
    Ответ написан
    Комментировать
  • Как праильно определить лотерейные билеты в соответствии с условиями задачи?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    И меня не устраивает "единственный" проблемный отрывок кода:
    if ndata[i] % 2 != 0 and nf % 2 != 0: lucky += 1

    Там вообще какой-то бред написан. При чем тут проверка на четность вообще?!

    Вам надо подсчитать солько чисел из trda есть в ndata и вывести Lucky, если насчитали хотя бы 3 (перечитайте же условие задачи).

    Соответственно должно быть что-то вроде
    if nf == ndata[s]: cnt+=1
    ...
    if cnt >=3:  print('Lucky')
        else: print('Unlucky')


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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Это производная комбинации: (f(g(x)))' = f'(g(x))*g(x).

    В вашем случае f(z) = z^2, g(w)=y-w*x.

    f(g(w)) = f(y-w*x) = (y - w*x)^2

    f'(z) = 2z, g'(w) = -x

    Подставляем в первую формулу:
    2(y - w*x)*(-x) - ровно то, что написано в книге.
    Ответ написан
    Комментировать
  • Как определить все способы выплаты суммы n с помощью монет достоинством в 1, 5, 10, 15, 20, 50 копеек?

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

    Решение через динамическое программирование:
    int Count(vector<int> coins, int n) {
      vector<int> cnt(n+1, 0);
      cnt[0] = 1;
      for (int i = 0; i < n; ++i) {
        for (int coin : coins) {
          if (i+coin <= n) cnt[i+coin] += cnt[i];
        }
      }
      return cnt[n];
    }
    Ответ написан
    9 комментариев
  • Как решить задачу?

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

    Понять те формулы можно примерно так. Съели мы первый куст, перешли ко второму. Теперь перенумеруем их, что останется? Та же самая задача, только с n-1 кустом. Только все кусты сдвинуты на k позиций и один номера у них на один уменьшены. Т.е. можно взять тот самый куст который при n-1 останется и найти, какой у него был номер до сдвига.
    Ответ написан
    Комментировать
  • Как для каждого делителя из одного массива найти делители из другого массива с остатком 0?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Если я правильно понял задачу, то надо подсчитать для каждого i
    sum_{j=1..n} (i%j?1:0) % 2.

    Или - есть n ламп в ряд изначально не горящих. Переключаются каждая первая, каждая вторая, третья и т.д. Вопрос - а какие лампы горят в конце.

    Разверните условие. Не переключайте каждую 1-ую, 2-ую и т.д. лампу, а подсчитайте для каждой лампы, сколько раз она будет переключена (потому что переключать их все по одной - это медленно. Надо как-то агрегировать вычисления)?

    Лампа будет переключена ровно столько раз, сколько делителей у ее номера. Иными словами - вам надо понять, четное ли количество делителей у каждого числа. Вспоминаем, что любое число представимо в виде разложения на простые множители: p1^k1*p2^k2* ... pm^km. Можно подсчитать количество делителей - это будет (k1+1)(k2+1)...(km+1), ведь каждое простое число может входить в делитель в степени от 0 до ki включительно.

    Теперь, в каком случае это число будет нечетным? Только если все ki четные. А это значит, что число - полный квадрат (все степени четные же. Берем корень, делим степени пополам, получаем целое число).

    Итого ответ - проставить true в массиве по всем индексам i*i.
    Ответ написан
    Комментировать
  • Найти алгоритм подсчета количеств множественного перекрытия интервалов, а также длительность таких интервалов?

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

    Для всех интервалов добавить в массив точек пары {время начала, +1} и {время конца+1, -1}. Это массив точек - границ вакансий на оси времени (прибавляем 1 к времени конца, потому что последняя секунда включена в интервал). Потом сортируем этот массив. Теперь одиним проходом можно выделить из массива все отрезки времени и сколько на них было открыто вакансий.

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

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

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

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

    Ваш код, например посчитает палиндромом строку "aab".
    Потом, тут еще зачем-то бинпоиск присобачен. Зачем, вообще непонятно.
    И еще ваше решение слишком медленное. Тут сложность порядка O(n^2 log n), что для 200000 ни в какие ограничения не влезет.

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

    решение

    Надо проверить, есть ли 2 подряд идущих одинаковых символа. Если есть - лексикографически минимальная такая пара - ответ.
    Иначе, надо проверить, есть ли тройка символов, где первый равен третьему. Из всех таких надо выбрать лексикографически минимальную.
    Если и таких нет, то ответ "-1".

    Задача решается за 2 не вложенных цикла.
    Ответ написан
    Комментировать
  • Что я не правильно делаю в тестовом задании?

    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, найдете ребро в уже обработанную вершину и среагируете на это, как на цикл. Хотя это не цикл.
    Ответ написан