Ответы пользователя по тегу Алгоритмы
  • Сколько существует различных уникальных подпоследовательностей из последовательности цифр длиной N?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Хорошая задачка, жаль, что я её раньше не заметил.
    Решение за O(N):
    static long nways(string seq) {
        long[] sum=new long[10];
        long cursum=1;
        foreach(char c in seq) {
            int d=c-'0';
            long x=cursum-sum[d];
            sum[d]=cursum;
            cursum+=x;
        }
        return cursum-1;
    }
    Ответ написан
    1 комментарий
  • Как составить программу "Расставить знаки арифметических операций и скобки чтобы выполнялось равенство"?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если в формулировке "расставить знаки, чтобы получить 100", то у меня осталось что-то такое:
    https://www.dropbox.com/s/vktq0cllod4d34s/LuckyTic...
    Ответ написан
    Комментировать
  • Как расставить шесть чисел так, чтобы сумма первых трех была примерно равна сумме остальных?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Отсортировать в порядке убывания, потом первые два элемента положить в разные кучки, а каждый следующий - в ту из кучек, где сумма на данный момент меньше. Работать будет не всегда (например, 25,25,20,20,9,1), зато не требует перебора.
    Ответ написан
    Комментировать
  • Есть ли реализация на любом языке программирования немного изменённой головоломки "Ханойская башня"?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Назовем столбики A,B,C (A в начале непустой).
    Введем операции:
    А->С() - переложить один диск с А на С
    top(A), top(C) - размер верхнего диска А или С. Если столбик пуст, то этот размер - MaxInt.
    В->С(К) - переложить с В на С все диски, размер которых меньше К (мы это можем сделать, если верхие диски А и С не меньше К)
    swap() - переставить столбики В и С (или переименовать их- нам ведь все равно, где окажутся диски)
    while(A) - цикл пока А не пуст.
    Тогда работает такой алгоритм:
    while(A){
      K=top(A);
      while(top(C) < K){
          B->C(top(C));
          swap();
      }
      A->C();
    }
    while(C){
      B->C(top(C));
      swap();
    }
    Ответ написан
    Комментировать
  • Есть ли алгоритм поиска элементов, однозначно определяющих судоку?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Боюсь, что количество вариантов, даже если искать минимальные, будет сравнимо с 2^80 (или, хотя бы, 2^60) - замучаетесь искать. Но если очень хочется, то можно попробовать так:
    - пусть у вас есть решатель (оптимизированный до предела), который по заданному набору клеток говорит ответ - 0 решений, одно или больше одного.
    Ищете перебором по дереву. Сначала про первую клетку предполагаете, открыта она или нет, потом для обоих случаев - про вторую, и т.д.
    Для любого набора открытых клеток в процессе перебора есть не меньше двух решений (иначе если оно одно - мы получили вариант ответа, а нуля не бывает, мы идём от известного решения).
    Когда мы решаем, открывать ли следующую клетку, делаем следующее:
    1) предположим, что мы не хотим её открывать. Откроем все клетки, идущие за ней, и посмотрим, сколько у нас решений. Если два - значит эту клетку нужно открыть обязательно, если нет, то можно и не открывать.
    2) посмотрим, нужно ли открывать эту клетку. Для этого подставляем в неё по очереди все цифры, кроме правильной, и смотрим, есть ли решение. Если хотя бы один раз оно есть, то клетку можно и открыть. Если ни разу решения не нашлось (например, эта клетка - 9-я в ряду, в котором уже открыто 8 клеток), то открывать её не нужно, поскольку иначе набор будет не минимальным.

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

    Mrrl
    @Mrrl
    Заводчик кардиганов
    А можете для какого-нибудь примера написать полученные прямую и обратную матрицы? Например, для A=M, B=N, D=K, C=(2,2) ?
    Ответ написан
  • Как построить матрицу проективного преобразования без сторонних библиотек?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Проще всего сначала построить матрицу преобразования из квадрата в 4-угольник, а потом взять обратную.
    Точки плоскости будем записывать, как принято в компьютерной графике, как строки: (x y 1) для собственной точки и (x y 0) для несобственной. Строка (c* c*y c) обозначает ту же точку, что и (x y 1) (при с!=0).
    Пусть матрица преобразования M=((a11 a12 a13) (a21 a22 a23) (a31 a32 a33)). Точка (u,v) квадрата переходит в точку (x,y) четырёхугольника, если для некоторого с!=0 выполняется
    (u v 1)*M=(c*x c*y c).
    Сначала возьмём вершину (0,0). Получим
    a31=c1*x1, a32=c1*y1, a33=c1. Видно, что мы можем взять a33=c1=1 (матрица тоже определена с точностью до пропорциональности), и у нас есть последняя строчка:
    a31=x1, a32=y1, a33=1.
    Теперь подставим остальные вершины. Получим 9 уравнений:
    a11+x1=c2*x2
    a12+y1=c2*y2
    a13+1=c2
    a11+a21+x1=c3*x3
    a12+a22+y1=c3*y3
    a13+a23+1=c3
    a21+x1=c4*x4
    a22+y1=c4*y4
    a23+1=c4
    Это квадратная система из 9 линейных уравнений. Её легко решить методом Гаусса. А можно вручную исключить все переменные, кроме c2,c3,c4, получить систему из 3 уравнений, решить каким-нибудь методом Крамера (через определители) и восстановить a11,a12,...,a23.
    Потом уже посчитать матрицу, обратную к построенной. Можно тоже через определители.
    Ответ написан
  • Поиск алгоритма в графе

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Насколько я понял, вам надо раскрасить вершины N-мерного куба (иначе как объяснить 2^N вершин и N рёбер?) в K цветов так, чтобы в 1-окрестности любой вершины (т.е. она сама плюс все её соседи) присутствовали все цвета.
    Задача очень похожа на коды, исправляющие ошибки. Но там обычно ставится двойственное условие - чтобы в 1-окрестности любой вершины каждый цвет встречался не более одного раза.
    Если N=2^m-1, то у задачи есть точное решение (c K=N+1), оно описывается кодом Хэмминга: все кодовые слова красим в один цвет, а множество вершин каждого другого цвета получаем из кодовых слов с помощью XOR с константой (своей для каждого цвета).
    Для других N надо думать. Но в этом есть смысл только если я правильно понял задачу.
    Ответ написан
    Комментировать
  • Алгоритм подсчета количества чисел в промежутке от А до B, сумма цифр которых четна?

    Mrrl
    @Mrrl
    Заводчик кардиганов

    На каждом отрезке от 10*n до 10*n+9 таких чисел ровно 5. Поэтому нам достаточно посчитать число таких полных отрезков, и обработать краевые отрезки. Пусть sumdig(n) - функуция, которая выдаёт остаток от деления суммы цифр n на 2. Тогда: int s0=(B/10-A/10-1)*5; int s1=(10+sumdig(A/10)-A%10)/2; int s2=(2+B%10-sumdig(B/10))/2; return s0+s1+s2;

    Ответ написан
    Комментировать
  • Можете подсказать алгоритм поиска наиболее часто встречающихся подстрок в тексте?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Я решал подобную задачу, заменяя самую часто встречающуюся пару символов новым символом (если она встречалась более трех раз) — правда, роль «символов» у меня играли слова. Самой длинной «часто встречающейся» подстрокой в «Мертвых Душах» оказалась «дама приятная во всех отношениях».
    Ответ написан
    Комментировать
  • Есть ли что-то быстрее BK-Tree?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Какого порядка расстояния, которые предполагаются в запросе?
    Я бы взял обычное бинарное дерево (ветвление по значению какого-нибудь бита; в каждом узле указано, по какому биту ветвиться). Тогда поиск(дерево,d)=поиск(поддерево с правильным значением бита,d)+поиск(поддерево с неправильным значением бита,d-1).
    Ответ написан
    9 комментариев
  • Эффективная передача кодов Хаффмана?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    В случае, если нужно передать действительно произвольный код Хаффмана, то выиграть почти ничего нельзя. Код определяется N-элементным вектором m-битных чисел (с условием, что все элементы различны) — это буквы, отсортированные по кодам, и расстановкой скобок в выражении из N операндов — это двоичное дерево. Число векторов равно (2^m)!/(2^m-N)!, число деревьев — C(N,2*N)/(4*N-2). Если N заметно меньше, чем 2^m, то первое из этих чисел можно оценить как 2^(m*N), а второе — как 2^(2*N)/(4*N-2)/sqrt(pi*N). Общее число бит в их произведении — примерно m*N+2*N.
    По мере приближения N к 2^m появляется возможность выиграть до 1.4*N бит (за счет того, что (2^m)!< (2^m)^(2^m).
    Ответ написан
    Комментировать
  • Эффективная передача кодов Хаффмана?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Что-то не так в условии. 24-битных чисел всего 16*10^6, их никак не наберется 2^9.
    И вопрос — нужно получить именно заданные коды Хаффмана, или достаточно, чтобы получились какие-нибудь эквивалентные по длине (а алгоритмы упаковки и распаковки сами договорятся, чтобы распакованные коды были именно теми, которые используются при упаковке основного сообщения)?
    Ответ написан
    5 комментариев
  • А какие есть алгоритмы для поиска максимального скопления точек на плоскости?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Задача сия мне попадалась неоднократно. В разных размерностях (от 1 до 6, причем шестимерное пространство было совсем не декартовым — оно описывало перемещения трехмерного пространства) и с разным числом точек, но всегда без точной формулировки. Хорошего решения я, кажется, не написал ни разу, каждый раз махал рукой и шел другим путем. Но мысли остались следующие.
    1) без понятия «масштаб» задача не имеет смысла. То есть, прежде чем решать ее, надо задаться неким «размером окошка», «радиусом размытия точки», «шириной серой зоны» и т.п.
    2) чаще всего этот размер заранее неизвестен. Если взять его с запасом, то результат будет правдоподобен, но неточен а если размер окажется слишком маленьким, то наоборот, найдется локальное скопление среди пустоты. Лучше всего, наверное, выбрать убывающую последовательность r1>r2>r3>r4… (например, для окна 2000х2000 это моут быть степени двойки от 128 до 8), найти квадрат со стороной r1, содержащий максимальное количество точек, в нем — квадрат со стороной r2, и т.д. В этом случае наш результат будет правдоподобен во всем диапазоне масштабов.
    3) Искать границы квадратов с точностью до пикселя смысла нет. Если мы ищем квадрат NxN, то достаточно перебрать квадраты с шагом N/4 по каждой координате. Например, если мы ищем квадрат 128х128 на плоскости размером 2000х2000, то достаточно рассмотреть 3600 возможных положений этого квадрата (вершина имеет координаты от 0 до 1888 с шагом 32). Завести целочисленный массив такого размера. Каждая точка попадает в 16 квадратов (или меньше) — увеличить 16 ячеек на 1. Найти максимальную — она даст стартовый квадрат.
    После этого в этом квадрате перебрать 25 квадратов 64х64, в максимальном из них 25 квадратов 32х32 и т.д.
    Если r1 выбран слишком маленьким, а плоскость была слишком большой, то вместо массива (который был 60х60) можно воспользоваться каким-нибудь деревом (для экономии памяти и времени на инициализацию).
    Не исключено, что имеет смысл просмотреть не одну последовательность квадратов, а несколько (выбрать 10 квадратов размером r1 с наибольшим весом, из всех квадратов со стороной r2, лежащих в них — 10 наибольших и т.д.) Но это будет писать сложнее, а сработает оно только если скопление выражено нечетко, а где-то есть разреженная туманность. Впрочем, в этой ситуации надо сразу уменьшать r1.
    Ответ написан
    Комментировать