← Все вопросы

Bitmask DP с состоянием 3^n: как считать ДП, где каждый элемент в одном из трёх состояний?

Задан 14 месяцев назад641 просмотров2 ответа
8

Встретил задачу, где каждый из n≤12n \le 12 объектов может быть в одном из трёх состояний (например: «не использован», «открыт», «закрыт»), и переход зависит от полной конфигурации. Битовая маска даёт только 0/1 на бит. Как кодировать состояние из трёх вариантов и сколько это стоит?

2 ответа

12
✓ Принятый ответ — помог автору

Когда у элемента три состояния, естественное кодирование — число в троичной системе: конфигурация = число от 00 до 3n−13^n - 1, где i-я троичная цифра ∈ {0,1,2} — состояние i-го объекта.

Извлечение/установка троичной цифры:

int n; // <= 12, т.к. 3^12 ~ 531441
vector<int> pow3(n + 1);
pow3[0] = 1;
for (int i = 1; i <= n; ++i) pow3[i] = pow3[i - 1] * 3;

// получить состояние объекта i из конфигурации mask
auto get = [&](int mask, int i){ return (mask / pow3[i]) % 3; };
// установить объекту i состояние v (0..2)
auto setv = [&](int mask, int i, int v){
  int cur = get(mask, i);
  return mask + (v - cur) * pow3[i];
};

vector<long long> dp(pow3[n], 0);
dp[0] = 1; // все объекты в состоянии 0
for (int mask = 0; mask < pow3[n]; ++mask) {
  if (dp[mask] == 0) continue;
  // ... переходы, меняющие состояние объектов через setv ...
}

Число состояний 3n3^n. Если переход перебирает объект (O(n)O(n)) — итого O(3n⋅n)O(3^n \cdot n). Для n=12n=12: 312≈5.3⋅1053^{12}\approx 5.3\cdot10^5, умножить на 12 → ∼6⋅106\sim 6\cdot10^6 — быстро. Но рост резкий: n=16n=16 даёт 316≈4.3⋅1073^{16}\approx 4.3\cdot10^7, а n=20n=20 — уже 3.5⋅1093.5\cdot10^9 (TLE). Поэтому троичные маски — для маленьких nn.

5

Альтернатива троичному кодированию — две битовые маски (по маске на каждое из двух «активных» состояний, третье = «ни в одной»). Тогда состояние = пара (maskA, maskB) с инвариантом maskA & maskB == 0. Битовые операции быстрее деления/модуля, которые нужны для троичных цифр (/ и % по pow3 — дорогие операции).

Компромисс: троичное кодирование компактнее по памяти (3n3^n ячеек против потенциально 2n×2n2^n\times2^n, если хранить пару наивно) и нагляднее, но %// в горячем цикле бьют по константе. Для n≤12n\le 12 разница не критична — бери что читаемее. Для больших nn и плотного перебора — две маски с битовыми операциями выигрывают по константе.

Ваш ответ

, чтобы ответить на вопрос.