← Все вопросы

Как решить задачу коммивояжёра (TSP) через ДП по битовой маске за O(2^n · n²)?

Задан 4 месяца назад524 просмотров2 ответа
12

Дано $n \le 18$ городов и матрица расстояний dist[i][j]. Нужно найти минимальный гамильтонов цикл (выйти из города 0, обойти все, вернуться в 0). Полный перебор перестановок — это $O(n!)$, для $n=18$ нереально. Слышал, что есть ДП по битовой маске. Как именно завести состояние и переходы?

2 ответа

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

Классический Held–Karp. Состояние dp[mask][i] = минимальная стоимость пути, который начался в городе 0, посетил ровно множество городов mask и сейчас стоит в городе i (бит i обязательно установлен в mask).

Идея: бит в маске = «город уже посещён». Переход — пробуем, куда пойти дальше из i в ещё не посещённый j:

const long long INF = 1e18;
int n; // <= 18
long long dist[20][20];
vector<vector<long long>> dp(1 << n, vector<long long>(n, INF));

dp[1][0] = 0; // стартуем в городе 0, посещён только он
for (int mask = 1; mask < (1 << n); ++mask)
  for (int i = 0; i < n; ++i) {
    if (dp[mask][i] == INF) continue;
    if (!(mask & (1 << i))) continue;
    for (int j = 0; j < n; ++j) {
      if (mask & (1 << j)) continue;        // j уже посещён
      int nmask = mask | (1 << j);
      dp[nmask][j] = min(dp[nmask][j], dp[mask][i] + dist[i][j]);
    }
  }

long long ans = INF;
for (int i = 1; i < n; ++i)
  ans = min(ans, dp[(1 << n) - 1][i] + dist[i][0]); // замыкаем цикл в 0

Сложность: состояний $2^n \cdot n$, переход перебирает $n$ соседей → время $O(2^n \cdot n^2)$, память $O(2^n \cdot n)$. Для $n=18$ это $\approx 2.6 \cdot 8.5 \cdot 10^5 \approx 8.5 \cdot 10^7$ — заходит. Обязательно long long для стоимости, иначе сумма рёбер переполнит int.

5

Частая ошибка — забыть, что город 0 фиксирован как старт, и перебирать старт тоже. Это лишний множитель $n$ и неверный ответ для путей (а не циклов). Если нужен гамильтонов путь (не цикл) с любым стартом/финишем, инициализируй dp[1<<i][i]=0 для всех i и ответ — min по dp[full][i] без замыкания на 0.

Ещё про память: $2^{18}\cdot 18$ long long ≈ 38 МБ — на грани. Можно хранить int если расстояния влезают, либо итерировать маски по числу битов и переиспользовать слои (но для TSP проще оставить полную таблицу).

Ваш ответ

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