Задать вопрос
  • Как найти решение для задачи?

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

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

    Если числа в задаче большие или нецелые, то сначала через бинпоиск или хешмап замените все координаты по X и по Y на их порядковый номер в отсортированном массиве (отдельно по каждой оси). Эта операция называется "сжатие координат". Теперь у вас все координаты целые числа от 0 до 2*n.

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

    Для этого каждый прямоугольник (X1, Y1, X2, Y2) создаст 2 события "открылся отрезок с Y1 до Y2 во время X1" и "закрылся отрезок с Y1 до Y2 во время X2". Эти все события вы сортируете по X и в таком порядке обрабатываете.

    Для отслеживания состояния прямой используйте дерево отрезков на минимум с отложенной суммой. Эта структура данных потребует массив на примерно 4n элементов для дерева с 2n листами. Каждая вершина будет хранить минимум на соответсвующем ей отрезке. При открытии прямоугольника вы должны добавить +1 на его отрезок. При закрытии - вычесть 1. Сначала открывайте прямоугольники, если время у событий одинаковое. Учитывайте это при сортировке. Так касающиеся по вертикали прямоугольники не создадут пустых мест.
    Если после обработки какого-либо события вы получили минимум в дереве отрезков 0 и следующее событие имеет большую координату по X - то какая-то часть оказалась не покрыта. Еще есть случай, если первое событие не X1=левая граница покрываемого прямоугольника, или последнее событие - не X2=правая граница всего прямоугольника. Тут тоже покрытия нет.

    Еще, аккуратно с целыми числами, чтобы если у вас 2 прямоугольника касаются горизонтальными сторонами - там пустое место не подсчиталось. Например, каждый лист дерева отвечает за единичный отрезок координат. Тогда изменяйте в дереве всегда отрезок от Y1 до Y2-1 для события с координатам Y1..Y2.
    Ответ написан
    7 комментариев
  • Как решить эту задачу?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    У вас 7 неизвестных и 3 уравнения. Так что однозначно вы найти значения переменных никак не сможете. Но и найти вам надо какую-то сумму. Есть шанс, что как-то комбинируя, складывая, вычитая и домножая левые части этих уравнений можно получить искомую сумму. Иными словами, вам надо вектор (16, 25..100) представить в виде линейной комбинации векторов (1, 2..49), (4, 9..64) и (9, 16..81). Обратите внимание, что там везде получаются суммы трех квадратов равны следующему.

    Вам надо подобрать такие 3 коэффициента, что x*n^2 + y(n+1)^2+z(n+2)^2 = (n+3)^2. Для n=1..7. У вас тут квадратные многочлены от n получаются, равны они в 7 точках, так что они должны быть равны вообще при любых n. Значит, вам надо раскрыть скобки, сгрупировать степени n и приравнять к 0 все коэффициенты.

    Так вы получите 3 уравнения на 3 переменные x, y, z.
    x+y+z=1
    2y+4z=6
    y+4z=9

    Отсюда получается x=1 y=-3 z=3

    В итоге получаете 1*1-3*12+3*123 - это ваш ответ.
    Ответ написан
    2 комментария
  • Как в typescript объединить ключи, и если появляются повторы, то сделать объединение типов?

    WblCHA
    @WblCHA
    Решение за один проход с поддержкой массивов и таплов. Глубина любая.

    https://www.typescriptlang.org/play/?#code/C4TwDgp...

    Основное отличие от решения Alexandroppolus, ключи собираются последовательно и не парсятся, но результат, понятное дело, одинаковый.

    type JoinKeys<T extends object> = JoinKeysDeep<T> extends infer J extends Record<
      string,
      unknown
    >
      ? { [Key in J extends J ? keyof J : never]: J extends J ? J[Key] : never }
      : never;
    
    type JoinKeysDeep<
      T extends object,
      K extends keyof T = JoinKeysDeep.GetKeys<T> & keyof T,
    > = K extends K & (string | number)
      ? T[K] extends object
        ? JoinKeysDeep<T[K]> extends infer J extends Record<string, unknown>
          ? J extends J
            ? Record<`${K | '*'}.${keyof J & (string | number)}`, J[keyof J]>
            : never
          : never
        : Record<K | '*', T[K]>
      : never;
    namespace JoinKeysDeep {
      export type GetKeys<T> = T extends unknown[]
        ? keyof T &
            (number extends T['length']
              ? // array
                number
              : // tuple
                `${number}`)
        : keyof T;
    }
    Ответ написан
    7 комментариев
  • Как мне продолжить сокращать формулу?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Два наблюдения:
    1) изначальная формула имеет вид (a=>b)*(b=>a).
    2) b = !a

    Отсюда в одно действие видно, что она вырождается в ложь: (a=>!a)*(!a=>a)

    Для второго наблюдения надо применить какой-то там закон, что !x+xy=!x+y
    Ответ написан
    4 комментария
  • Как решить проблему с типизацией getValue?

    WblCHA
    @WblCHA
    https://www.typescriptlang.org/play?#code/C4TwDgpg...

    type ExtractValuesFromSubKeys<T extends object, SK extends string, K extends keyof T & string = keyof T & string> = K extends `${string}${SK}${string}` ? T[K] : never
    
    function getValue<T extends object>(obj: T, isIncludeKey?: false): <K extends keyof T>(key: K) => T[K]
    function getValue<T extends object>(obj: T, isIncludeKey: true): <SK extends string>(subKey: SK) => ExtractValuesFromSubKeys<T, SK>[]
    function getValue<T extends object>(obj: T, isIncludeKey?: boolean) {
      return (keyOrSubKey: string) => {
        if (!isIncludeKey) {
          const key = keyOrSubKey as keyof T
    
          return obj[key];
        }
    
        const subKey = keyOrSubKey
    
        return Object.keys(obj).filter(key => key.includes(subKey)).map(key => obj[key as keyof T])
      }
    }
    
    const getter = getValue({ aaa: 1, bbb: '2', ccc: [3], abc: null }, true);
    
    const ttt1 = getter("a") // (number | null)[]
    const ttt2 = getter("bb") // string[]
    const ttt3 = getter("ccc") // number[][]
    const ttt4 = getter("unknown") // never[]
    Ответ написан
    4 комментария
  • Почему переменная класса становится undefined при обращении из метода?

    Mike_Ro
    @Mike_Ro Куратор тега JavaScript
    Python, JS, WordPress, SEO, Bots, Adversting
    Стрелочная функция не имеет своего this, соответственно возьмет его из скоупа выше:
    MyFunc = (e) => {
      console.log("MyVariable: " + this.MyVariable); // oh yes
    }
    Ответ написан
    Комментировать
  • Как из первых N натуральных чисел составить максимальное количество пар, суммы которых являются простыми?

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

    А главная проблема вашего решения в том, что оно "жадное". Вы жадно берете первую папавшуюся пару, как будто так будет оптимально. Но это не так. Например, если у вас остались гири весами 2, 3, 4, 5, то брать пару 2+3 = 5 нельзя, ведь тогда оставшиеся 4+5=9 дадут не простое число.

    Это действительно можно решать через задачу о паросочетании как сказал Alexandroppolus. Вот только все алгоритмы работают за O(N^3), так что для N=500000 это решение будет работь очень долго.

    Но в вашем случае граф очень специфичный, так что надо придумать какой-то шаблон. Например, можно взять максимальное простое число P <= N+1 и набирать его всеми возможными парами (1+(P-1), 2+(P-2)). В итоге у вас остнется одна нечетная гиря (P+1)/2 и все гири >=P. Например, если N+1 простое - то это оптимальный способ.

    Я бы посоветовал написать решение через максимальное паросочетание и прогнать его для всех N <=200. Получите оптимальные ответы, посмотрите на них. Поищите какой-то паттерн в количестве пар, потом попробуйте придумать способ такое количество набрать.

    И еще немного покритикую ваш код.
    f==true. Зачем? Можно написать if(f). А вообще вам тут f не нужно, пишите if(Check(i+j)).

    Вместо
    if(a[j]!=0&&a[i]!=0&&i!=j) { 
      ...
    }

    стоит использовать "ранний выход":
    if(!a[i] || !a[j] || i == j) continue; 
    ...


    Так вложенность и сложность кода меньше. Его проще читать и понимать.

    Вместо прверки, что i != j, можно внутренний цикл гнать от n до i+1. Вам же не надо перебирать отдельно пары 1+10 и 10+1? Всегда считайте, что первое число меньше второго в паре.

    Ну, и отступы, ради всего святого, расставьте аккуратно. Любой редактор кода умеет их делать автоматически.

    Edit:
    Совместо с Alexandroppolus в комментариях придумали следующее решение: Берете минимальное простое число, большее N. Обзовем его P. Тогда берем пары N+(P-N), (N-1)+(P-N+1)... и т.д. В этих парах одно число четное, другое нечетное. И всего они покроют отрезок четной длины без дырок. Потом задача сводится к такой же, только уже с максимальным числом не N, а P-N-1.
    Эта жадность работает, потому что она всегда соберет максимально теоретически возможное количетсво пар. Может только одна 1 в конце останется.
    Ответ написан
    6 комментариев
  • Как найти последовательность букв через графы?

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

    Тогда постройте граф на n*m*l вершин. Каждая вершина соответствует тройке (x, y, k) и означает, что мы как-то походили по полю и оказались в координате (x, y), при этом набрав первые k символов искомой строки. Переходы в графе тут такие: от каждой вершины можно пойти в 4 соседние по координатам, а количество символов увеличивается на 1, если в следующей вершине читается следующая буква строки. Т.е. переходы вида (x, y, k) -> (x+1, y, k + (s[k] == grid[x+1][y] ? 1 : 0)); (x, y, k) -> (x, y+1, k + (s[k] == grid[x][y+1] ? 1 : 0)) и еще 2 на минус (если x,y не у стенки поля, естественно - некоторые переходы могут отсутствовать).

    Ищите кратчайшее расстояние из вершины (x0, y0, s[0] == grid[x0][y0] ? 1 : 0) в любую вершину с третим индексом равным l (т.е. любое место, где вы прочтете всю искомую строку). Поскольку в графе все ребра длины 1, можно запускать bfs. Граф строить и охранить не надо, можно эти 4 перехода вычислять неявно. Кладите в очередь тройки чисел, вынимайте их оттуда и вычисляйте до 4 соседних троек, которые гужно тоже сложить в очередь обхода в ширину. Удобно завести 2 костантных массива на 4 элемента, которые будут хранить приращения по x и по y в каждую их четрех сторон. Тогда не будет много дублируемого кода.

    Еще понадобится, таки, массив на n*m*l для хранения расстояния, или хотя бы пометок о том, что вершина уже в очередь была положена.
    Ответ написан
    2 комментария
  • Почему мой код считается медленным?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Ваш алгоритм работает за O(n^2). Вы для каждого числа в массиве считатете, сколько раз оно туда входит проходя по массиву через nums.count(i). Оптимальное же решение работает O(n). Надо в хеш-таблице подсчитать, сколько раз каждое число встречается, потом через алгоритм QuickSelect выбрать k-ый c конца элемент.

    Ну, или можно за O(n log n) отсортировать массив и потом за один проход подсчитать сколько раз каждое число встречается. Дальше можно второй раз отсортировать по количеству вхождений и выдать k-ый элемент. Это решение тоже пройдет.
    Ответ написан
  • Как работает замыкание в js?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Каждый вызов createCounter() создаёт и возвращает новую функцию. С которой в комплекте идёт свой новый «чемодан» замыкания, в котором лежит своя переменная counter.
    Каждый раз отдельная-новая.

    откуда берется counter при втором вызове console.log(z())

    Из того же «чемодана» от первого вызова createCounter()
    Ответ написан
    2 комментария
  • Как убрать обработчик события при вызове функции?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Если обработчик должен отрабатывать ровно 1 раз, то лучше передавать опцию once:
    box.addEventListener('mousemove', move, { once: true });
    https://developer.mozilla.org/en-US/docs/Web/API/E...
    Ответ написан
    Комментировать
  • Как сделать динамическую проверку существования ключа при создании функции?

    WblCHA
    @WblCHA
    Taulan Khatuaev, pavelpressf, потрясающие ответы с эни (нет).

    function helloFunc<T extends object, K extends keyof T>(arg: {
      nameKey: K,
      object: T
    }) {
      console.log(`Привет,  ${arg.object[arg.nameKey]}`)
    }


    https://www.typescriptlang.org/play?#code/GYVwdgxg...
    Ответ написан
    1 комментарий
  • Как превратить подстроку вида "min ( a, b )" в "a min b"?

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

    Решение поумнее, за линию:

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

    Итак, функция, опять же, проверяет, что есть min. Если нет - возвращает всю строку до первой "," или непарной ")" - нужен счетчик открытых скобок.
    В противном случае, пропускает "min", "(". Рекурсивно преобразовывает от этой позиции. Потом смотрит, что после позиции, которую вернула рекурсивная функция, лежит ",". Иначе - ошибка. Добавляет min к ответу, Рекурсивно преобразовывает оставшееся и добавляет к ответу. Проверяет, что дальше после позиции где закончила обработку рекурсивная функция лежит ")". Возвращает следующую за этим позицию.

    Можно не возвращать преобразованную строку, а сразу все функции могут просто дописывать свой ответ в какое-то одно место. А возвращать int - индекс конца. Ну, или длину обработанной части строки - так даже понятнее.
    Ответ написан
    1 комментарий
  • В чем принципиальная разница между сигнатурой индекса и утилитой Record?

    Zhuroff
    @Zhuroff Автор вопроса
    Пока удалось найти, что посредством сигнатуры можно запилить циклический тип. А Record этого не позволяет. На практике подобное вроде пока не пригождалось, но мало ли.
    type SomeType1 = { [index: string]: SomeType1 | null }
    type SomeType2 = Record<string, SomeType2 | null> // Type alias 'SomeType2' circularly references itself.
    Ответ написан
    Комментировать
  • Как вычислить мат.выражение представленное в виде дерева?

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

    Griboks
    @Griboks
    Всё намного проще. Вам нужно решить тривиальное уравнение вида y=kx. Ответ вас удивит...
    Ответ написан
    3 комментария
  • Base64 формат картинки, что делать на фронтенде?

    MrDecoy
    @MrDecoy Куратор тега JavaScript
    Верставший фронтендер
    что делать на фронтенде

    Пойти к бэкендеру и сказать: ты шо, дурак, зачем нам 470киллобайт не кешируемых данных по сети каждый раз гонять?!
    И пинать чтоб присылал ссылку.
    (а на деле я так понимаю там не одна картинка такая и не кешируемых данных в разы больше гоняется, что вообще ужас)

    На серьёзном хайлоад проекте это была бы, условно, смерть серверам по трафику.
    Но клиенты с не самым хорошим интернетом отлетели бы ещё раньше. А не самый хороший это даже мобильный 4g в средних условиях приёма сигнала. 470кб это очень, очень много для всего лишь одного запроса. Тяжелее запрос - дольше время до отрисовки.
    Ну и мобильный интернет далеко не у всех безлимитный. А не в России и подавно.

    А ещё есть лимиты на длину строки. В разных движках причём разные. Так что, теоритически, наверное, можно упереться в лимит строки и картинка не отобразится или будет битая.
    Ответ написан
    7 комментариев
  • Как правильно решить задачу, используя формулу комбинаторики?

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

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

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

    Покрасьте поле как шахматную доску. Черные и белые клетки. Каждая доминошка будет лежать на двух соседних белой и черной клетках. Постройте граф. Назначьте каждой клетке вершину, а ребра проведите между соседними клетками, если там нет перегородки. Граф - двудольный, ведь черные клетки окружены белыми, а белые - черными. Любое заполнение поля доминошками будет идентично паросочетанию в этом графе и наоборт. Найдите максимальное паросочетание: если оно не полное, то поле покрыть нельзя. Иначе ребра в паросочетании будут местами, куда надо класть доминошки.

    Вот статья с описанием алгоритма и реализацией.

    Это будет решение за O(n^2m^2).

    Другое решение, которое будет быстрее, если одно из измерений очень маленькое, а второе очень большое - динамическое программирование по профилю. Гуглите эти слова. Это сложнее реализовать, но зато будет работать за O(4^n m)

    Edit:
    Alexandroppolus в коментариях предложил использовать Алгоритм Хопкрофта — Карпа для поиска паросочетания, что для данного графа будет быстрее предложенного мной алгоритма Куна и будет работать за O(nm sqrt(nm)) вместо O(n^2 m^2)
    Ответ написан
    7 комментариев
  • Как в результате вычитания получить ноль максимально быстро?

    Griboks
    @Griboks
    Я долго изучал высшую математику и теперь наконец готов поделиться с вами наибыстрейшим способом получить нуль: x=0. Это даже быстрее Saboteur .
    Ответ написан
    1 комментарий