Динамика по подмножествам
Состояние — маска использованных элементов. Коммивояжёр, разбиение на пары, покрытие множества и замеры, до каких n это живёт.
2 мин
Если в задаче есть до двадцати объектов и важно, какие из них уже использованы, состоянием динамики становится маска.
Работает это потому, что подмаска в порядке возрастания встречается раньше маски: обычный цикл for (int mask = 0; mask < (1 << n); mask++) уже даёт правильный порядок вычислений, и ни рекурсия, ни топологическая сортировка не нужны.
Коммивояжёр
Классика жанра. — наименьшая стоимость пути, который начался в вершине 0, посетил ровно вершины из и закончился в .
dp[1][0] = 0;
for (int mask = 1; mask < (1 << n); mask++)
for (int v = 0; v < n; v++) {
if (dp[mask][v] >= INF || !(mask >> v & 1)) continue;
for (int to = 0; to < n; to++) {
if (mask >> to & 1) continue;
int next = mask | (1 << to);
dp[next][to] = std::min(dp[next][to], dp[mask][v] + d[v][to]);
}
}
Стоит это по времени и по памяти. Замеры: — 23 мс, — 85 мс, — 405 мс. То есть двадцать вершин проходят с запасом, а двадцать две уже нет.
Память при : чисел по четыре байта — 80 мегабайт. Это уже близко к типичному ограничению, и часто именно память, а не время, ставит границу.
Другие типовые задачи
| задача | состояние |
|---|---|
| разбить на пары с наименьшей суммой | маска использованных |
| назначить работ исполнителям | маска занятых исполнителей |
| покрыть множество наименьшим числом подмножеств | маска покрытого |
| раскрасить граф в наименьшее число цветов | маска покрашенных |
В задаче о разбиении на пары полезен приём: не перебирать оба элемента пары, а всегда брать наименьший невыбранный. Это убирает лишний множитель .
int first = __builtin_ctz(~mask & full); // наименьший свободный
for (int second = first + 1; second < n; second++)
if (!(mask >> second & 1)) { ... }
Когда не хватает
Иногда состояние — не «какие использованы», а «сколько чего осталось». Если объектов много, но типов мало, вместо маски берут вектор счётчиков — и получается динамика по профилю.
А если нужно перебрать разбиения на подмножества, помогает перебор подмасок: , что при ещё живо.