Маска как множество
Число из n битов — это подмножество n-элементного множества. Словарь перевода и перебор всех подмножеств.
2 мин
Главное применение битов в олимпиадных задачах: число — это множество. Бит номер означает «элемент входит».
При всех подмножеств — их можно перебрать. При уже , тоже иногда проходит. Дальше — нет.
Словарь
| множества | маски |
|---|---|
a & b |
|
a | b |
|
a ^ b |
|
a & ~b |
|
| дополнение в битах | a ^ ((1 << n) - 1) |
(a >> x) & 1 |
|
(a & b) == a |
|
__builtin_popcount(a) |
|
| пустое множество | 0 |
| всё множество | (1 << n) - 1 |
Обратите внимание на дополнение: ~a перевернёт все биты типа, включая те, которых в задаче нет. Дополнение в пределах битов — это a ^ ((1 << n) - 1).
Перебор всех подмножеств
for (int mask = 0; mask < (1 << n); mask++) {
for (int i = 0; i < n; i++)
if (mask >> i & 1) { ... } // элемент i входит
}
Стоит это . Если нужны только входящие элементы, а множество разреженное, быстрее перебирать единицы:
for (int rest = mask; rest; rest &= rest - 1) {
int i = __builtin_ctz(rest); // очередной входящий элемент
...
}
Порядок перебора
Полезное свойство: если перебирать маски по возрастанию, то любая подмаска встретится раньше самой маски. Это позволяет считать динамику по подмножествам простым циклом for (int mask = 0; ...), без рекурсии и без топологической сортировки.
Обратное тоже верно: перебор по убыванию гарантирует, что надмаска встретится раньше.
Смежное
- Подмаски — перебор подмножеств одного множества;
- Динамика по подмножествам — главное применение;
- Сколько стоит перебор — когда маски заменяют рекурсию.