ДП с битовыми масками творит чудеса

Правка ru1, от micklepru, 2015-07-25 00:47:57

Привет, Codeforces!

Помогите разобраться с алгоритмом.

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

Решение "в лоб" — просто пробежаться по всем перестановкам цифр числа N и проверить каждую. Это дает нам N! операций, что слишком медленно. Интуитивно понятно, что нам в любом случае прийдется проверить каждую из перестановок и я не могу понять, как мы этого избегаем.

Используем ДП с битовыми масками: dp[i][j] — количество чисел, полученных перестановкой некоторых цифр N(которые определены маской i), и остача от деления на M равна j. Например, N=12345, dp[01101][2] — количество чисел, сложенных из 2,3,5 (такие как 352), и остача от деления на М равна 2 (M=50, 352%50==2). Для каждой маски i мы пробегаемся(переменная k) по всем еще не использованным цифрам((i & 1<<k)==0) и добаляем их в конец каждой перестановки(i | 1<<k). У старой перестановки остаток j, у новой — (j*10+N[k])%M, где N[k] — k-я цифра N. Увеличиваем dp[i | 1<<k][(j*10+N[k])%M] на dp[i][j].

Вот код для лучшего понимания: 12205724 Заметки: timesRepeat используется, чтоб избавится от повторяющихся перестановок, которые возникнут если в N есть повторяющиеся цифры. (i||N[k]) для избавления от лидирующих нулей

Используя ДП, мы снизили сложность от N! до (2^N)*N, что позволяет нам сдать задачу.

Рассмотрим данное решение: выглядит так, что мы пробегаемся по всем возможным перестановкам. Например, маска 0110 может быть получена как 0100 добавить третюю цифру в конец или же к 0010 добавить вторую цифру в конец. Для каждой маски мы добавляем каждую цифру в каждое место. Я не пойму, как мы проделываем меньше работы, но рассматриваем все перестановки.

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

Теги дп, битовые маски, перестановки, перебор

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
ru2 Русский micklepru 2015-07-25 15:07:53 117
en3 Английский micklepru 2015-07-25 15:05:11 100 Tiny change: '#### UPD:\n_Thank you' -
en2 Английский micklepru 2015-07-25 01:11:12 11 Tiny change: 'nused yet numbers(`(i & 1<' -> 'nused yet digits(`(i & 1<'
ru1 Русский micklepru 2015-07-25 00:47:57 2150 Первая редакция перевода на Русский
en1 Английский micklepru 2015-07-25 00:28:07 1940 Initial revision (published)