• Как с помощью регулярного выражения определить регистр?

    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 вершины, или разворачивайте кусок пути). Еще можно какую-нибудь наивную жадность сделать: Каждый раз следующей берите самую близкую из не обойденных пока вершин. Повторю, что это будет не оптимальное решение, а лишь какая-то аппроксимация.
    Ответ написан
    Комментировать
  • Существуют ли алгоритмы сжатия случайных данных с конечным алфавитом?

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

    Советую посмотреть в сторону Burrows-Wheeler transform и потом попробовать RLE или LZ присобачить сверху. Может, ваши данные будут им хорошо сжиматься.

    Еще вам тут сильно помогло бы что-то вроде Base64 encode/decode. Допустим у вас k символов в алфавите. Значит каждый символ несет log_2(k) бит. И если у вас символов N, то ваша входная строка содержит N*log_2(k) бит информации. Округлите это число вверх и сгенерируйте столько битов. Это фактически преобразование из k-ичной системы счисления в двоичную. На больших строках будет тормозить, потому что пока мне не очевидно, как для произвольного k делать преобразование быстро, а не делить большое число на 2 с остатком. Если только у вас k не степень двойки, тогда как в base64 можно быстро преобразовывать по блокам.

    Потом можно эту битовую строку сжать каким угодно алгоритмом (разбить на блоки, скажем, 8 бит и хоть хаффманом, хоть lz). Потом надо сжатую битовую строку преобразовать назад в k-ичную систему счисления.

    Можно комбинировать сжатие на исходном тексте и запись произвольной битовой строки в вашем алфавите. Например после BW-transform вы гоните LZ на тексте из цифр. LZ для эффективности надо уметь писать произвольные битовые строки. Вот вы где-то в памяти отдельно собираете новые символы, которые замыкают новые строки-эталоны (цифры в вашем примере), и отдельно битовую строку. Потом эту строку переведите в k-ичную систему счисления и запишите перед просто символами (как-то закодировав ее длину в скольки-то первых символах заголовка).
    Ответ написан
    Комментировать
  • Запрет на ввод букв в консоли на C++ для вещественных?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Читайте строку std::string. Проверяйте,что в ней максимум 1 '-' в самом начале, далее первая цифра не '0' (если второй не ',', есть максимум один символ ',' и он не первый. Потом используйте stringstream чтобы прочитать double.
    Ответ написан
    Комментировать
  • Как создать массив строк случайной длины из предложенных строк?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Нужен генератор случайных чисел. в JS есть Math.random();

    Далее, чтобы сгенерировать один массив пройдитесь по всему массиву строк и добавляйте текущую строку к ответу, если random() < kThreshold. kThreshold подбирайте исходя из того, какой длины вам нужны случайные массивы. В среднем в ответе будет kThreshold*array.length() элементов. Т.е. при kThreshold=0.5, в среднем сгенерированные массивы будут содержать по половине всех строк.

    Минус этого метода, что чаще всего сгенерированные массивы будут средней длины. Массив из всех строчек или из одной единственной строчки будут маловероятны, но это может и не минус - зависит от того, зачем вам это надо.
    Ответ написан
  • Что такое Heap (куча)?

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

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

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

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если пишите что-то посложнее формочек, то может и пригодится.

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

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

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

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

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

    А уж если вы разработчик компилятора или какого-нибудь медиа кодека, то там дискретка лезет из всех щелей (теория языков, формальные парсеры, дискретное преобразование Фурье).
    Ответ написан
    Комментировать
  • А вы правда умеете программировать?

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

    Сверху к этому добавляется знание структур данных и алгоритмов и хорошее знание языка программирования, его библиотеки и конструкций. Но эти все знания второстепенные. Не нужно все это знать наизусть. Достаточно иметь представление, что в языке есть vector для массивов и set для множеств. Конкретные функции можно подсмотреть в справке или гугле.

    По мере приобретения опыта эти стандартные части языка и алгоритмы станут базой и запомнятся.

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

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

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

    Функция берет из текущего массива один из элементов в цикле и добавляет его к ответу и запускается рукурсивно от следующего массива. Еще функция не берет ничего из текущего массива и запускается рекурсивно.

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

    Псевдокод примерно такой (не php, чтобы не позориться):

    function Generate(result, array_index, arrays) {
      if(result >= len(arrays)) {
        print(result)
        return
      }
      if (min_length - len(result) < len(arrays)-array_index-1) {
        Generate(answer, array_index+1, arrays);
      }
      if (len(answer) < max_length) {
        for i = 0...len(arrays[array_index]) {
          answer.push_back(arrays[array_index][i]);
          Generate(answer, array_index+1, arrays);
          answer.pop_back();
        }
      }
    }
    Ответ написан
    Комментировать
  • Как доказать равенство множеств?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Формально по законам логики и определениям.
    Что значит какой-то элемент принадлежит правому множеству?
    x in A\(A\B), тогда и только тогда, когда x in A И x not in (A\B),
    Чтобы вторая половина после И выполнялось, надо наложить отрицание не x in (A\B) что тоже самое, что (x in A И x not in B). По законам логики это будет (x not in A ИЛИ x in B). добавляем к этому первую часть и раскрываем скобки:

    x in A И (x not in A ИЛИ x in B) = (x in A И x not in A) ИЛИ (x in A И x in B).

    Первая часть - всегда ложна и сокращается, остается только (x in A И x in B), что по определению x in A и B. Мы везде использовали только эквивалентность и из определения, что элемент принадлежит правому множеству, получили определение, что элемент принадлежит левому множеству. Значит эти 2 множества равны.
    Ответ написан
    Комментировать
  • Наиболее подходящий алгоритм для поиска цены?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Чтобы сделать бинарный поиск, вам нужен массив объектов с ценами и названиями. Что-то вроде
    [1=>{cost:1, name:"name1"} .... 117=>{cost:55233, name:"name555"}]
    (не уверен, что правильно описал на js).

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

    Можно поискать какие-нибудь js библиотеки с нормальным ассоциативным массивом, который это умеет.
    Будет также работать за логарифм.
    Ответ написан
    Комментировать
  • Как найти вектор сигнала в плоскости?

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

    У вас будет 3 переменные - координаты источника сигнала (x, y) и время отправки сигнала (t).
    Вам даны координаты трех точек (x_i, y_i, i=0..2), три времени получения сигнала (t_1, t_2, t_3) и скорость сигнала (v).

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

    Уравнения, что сигнал проходит заданное расстояние за заданное время с известной скоростью:

    (x_i-x)^2+(y_i-y)^2 = ((t-t_i)*v)^2

    Можно время считать не в секундах, а в 1/v, тогда уравнения чуть упрощаются (коэффициент перед t^2 везде 1, а не v^2).

    Можно решать аналитически. Вычтите первое уравнение из двух остальных. У вас получится 2 линейных уравнения с тремя неизвестными x, y, t. Считайте, что t - это константа и решите уравнения относительно x и y (Через определители, или метод Краммера). У вас будет какая-то линейная зависимость x и у от t (большие формулы, да). Можно упростить вычисления, если cначала записать уравнения в виде A1x+B1y=C1+D1t.

    Потом подставьте эти зависимости в первое уравнение и у вас будет квадратичное уравнение на t.

    Решите его. Подставьте t в известные уравнения для x и y - и вот ваш центр (заодно вы знаете, когда был отравлен сигнал).

    Из двух значений t, одно будет в будущем (положительное), его надо будет отбросить.
    Ответ написан
    2 комментария