#include <bits/stdc++.h> using namespace std; usi...
Создано: 15 апреля 2026 г.
Создано: 15 апреля 2026 г.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
int a[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> a[i][j];
}
}
int pw = 1;
vector <int> p(n + 1, 1);
for (int i = 1; i <= n; i++) {
pw *= 2;
p[i] = pw;
}
vector <int> ans(pw, 1e9);
ans[1] = 0;
vector <int> par(pw);
par[1] = 1;
vector <int> used(n, 0);
vector <int> lst(pw);
lst[1] = 0;
for (int i = 2; i < ans.size(); i++) {
if (i % 2 == 0) {
continue;
}
int k = i;
for (int j = n; j >= 0; j--) {
if (k >= p[j]) {
used[j] = i;
k -= p[j];
}
}
int mn = 1e9;
int pr = -1;
for (int j = 0; j < n; j++) {
if (used[j] != i) {
continue;
}
if (a[lst[i - p[j]]][j] != 0 && mn > ans[i - p[j]] + a[lst[i - p[j]]][j]) {
mn = ans[i - p[j]] + a[lst[i - p[j]]][j];
pr = i - p[j];
lst[i] = j;
}
}
ans[i] = mn;
par[i] = pr;
}
n = pw;
if (ans[n - 1] == 1e9) {
cout << -1;
return 0;
}
vector <int> w;
int x = n - 1;
while (par[x] != x) {
w.push_back(lst[x]);
x = par[x];
}
w.push_back(lst[x]);
cout << ans[n - 1] << "\n";
for (int i = w.size() - 1; i >= 0; i--) {
cout << w[i] + 1 << " ";
}
}
Сдать решение задачи A-17 стульев
Ограничение времени: 1 с
Ограничение реального времени: 5 с
Ограничение памяти: 64M
Задача A: 17 стульев
Условие задачи (pdf)
64 мегабайта
Остап Бендер снова пытается получить причитающиеся драгоценности, но на этот раз они были заперты в шкатулке, для открытия которой необходимо иметь
N
ключей. По закономерной случайности каждый из ключей был спрятан в одном из
N
стульев, распроданных на недавнем аукционе. После аукциона эти стулья были развезены в
N
городов.
И вот теперь Остап решился на новую безумную затею: заехать в каждый из городов и, провернув в каждом из них аферу, выкрасть необходимые ключи. Чтобы избежать конфликтов с недоброжелателями, Остап не хочет больше одного раза появляться в каком-либо городе. Также у Остапа есть список цен за проезд между каждой парой городов. Изначально Остап находится в городе под номером 1 и после посещения всех городов может незаметно скрыться из этой страны.
Помогите Остапу найти порядок посещения городов, при котором ему потребуется потратить как можно меньше средств на странствия, и тогда, возможно, он поделится с Вами добытыми бриллиантами.
Формат входных данных
Первая строка содержит единственное число
N
— количество городов (
1
≤
N
≤
17
).
Следующие
N
строк содержат по
N
целых неотрицательных чисел.
j
-тое число в
i
-й строке означает стоимость проезда из города
i
в город
j
(
0
≤
a
i
j
≤
100
). Если
a
i
j
0, то проезд стоит
a
i
j
рублей, иначе — это означает, что из города
i
в
j
невозможно проехать напрямую.
Формат выходных данных
В первой строке выведите минимальную сумму денег, необходимую для посещения всех городов Остапом. В следующей строке выведите
N
чисел — порядок посещения городов, при котором эта сумма достигается. Если затею Остапа невозможно вывести, то в единственной строке выходного файла выведите число -1.
Примеры
Входные данные
3
0 3 2
3 0 6
2 6 0
Выходные данные
8
1 3 2
Входные данные
5
0 6 4 0 0
6 0 7 0 7
4 7 0 0 0
0 0 0 0 2
0 7 0 2 0
Выходные данные
20
1 3 2 5 4
почему у меня ва
У тебя WA, потому что состояние динамики выбрано неверно.
Сейчас у тебя хранится:
ans[mask] — минимальная стоимость пройти города из masklst[mask] — последний город в этом оптимальном путиИ ты считаешь, что этого хватает. Но не хватает.
Для такой задачи надо различать:
То есть правильное состояние:
где mask — множество посещённых городов, v — последний город.
dp[mask] без последней вершины ломаетсяПотому что для одного и того же множества городов могут быть:
А ты оставляешь только один вариант через lst[mask] и выбрасываешь остальные. Из-за этого оптимальный глобальный путь может потеряться.
Пусть для некоторой маски:
mask стоит 10 и заканчивается в A11 и заканчивается в BЕсли потом:
A в новый город ехать за 100B в новый город ехать за 1то:
A получится 110B получится 12Оптимум — через второй путь, но твой код его уже выкинул.
Нужно обычное DP по подмножествам:
dp[mask][v] — минимальная стоимость попасть в v, посетив ровно maskdp[1][0] = 0, потому что начинаем в городе 1cppdp[mask | (1 << to)][to] = min(dp[mask | (1 << to)][to], dp[mask][v] + a[v][to])
Вот это:
cppint a[n][n];
в C++ не стандартно. У некоторых компиляторов проходит как расширение, но лучше писать:
cppvector<vector<int>> a(n, vector<int>(n));
Это не причина WA, но лучше исправить.
cpp#include <bits/stdc++.h> using namespace std; const int INF = 1e9; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<vector<int>> a(n, vector<int>(n)); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> a[i][j]; } } int FULL = 1 << n; vector<vector<int>> dp(FULL, vector<int>(n, INF)); vector<vector<int>> parMask(FULL, vector<int>(n, -1)); vector<vector<int>> parLast(FULL, vector<int>(n, -1)); dp[1][0] = 0; // стартуем из города 1 for (int mask = 1; mask < FULL; mask++) { if ((mask & 1) == 0) continue; // маршрут должен содержать город 1 for (int v = 0; v < n; v++) { if (!(mask & (1 << v))) continue; if (dp[mask][v] == INF) continue; for (int to = 0; to < n; to++) { if (mask & (1 << to)) continue; if (a[v][to] == 0) continue; int nmask = mask | (1 << to); int nd = dp[mask][v] + a[v][to]; if (nd < dp[nmask][to]) { dp[nmask][to] = nd; parMask[nmask][to] = mask; parLast[nmask][to] = v; } } } } int fullMask = FULL - 1; int best = INF, last = -1; for (int v = 0; v < n; v++) { if (dp[fullMask][v] < best) { best = dp[fullMask][v]; last = v; } } if (best == INF) { cout << -1 << '\n'; return 0; } vector<int> path; int mask = fullMask; while (last != -1) { path.push_back(last); int pm = parMask[mask][last]; int pv = parLast[mask][last]; mask = pm; last = pv; } reverse(path.begin(), path.end()); cout << best << '\n'; for (int v : path) { cout << v + 1 << ' '; } cout << '\n'; return 0; }
Для N <= 17 это нормально:
2^N * N2^N * N * NТо есть примерно:
для 17 проходит.
Потому что ты пытаешься решить задачу гамильтонова пути через dp[mask], а нужно через dp[mask][last].
Иначе теряется информация о том, в каком городе закончился путь.
Могу ещё показать маленький контрпример именно к твоему подходу, на котором он даёт неверный ответ.
объясни как устроен дп по подмаскам
Окей, давай разберём DP по подмаскам максимально понятно и “с нуля”, без магии.
Если N ≤ 17, удобно кодировать множество городов числом (битовой маской):
N городов → N битовi посещён → бит i = 1Пример (n = 4):
mask = 0101₂ = 5
Это значит: посещены города 0 и 2
Мы перебираем все подмножества городов и считаем ответ для каждого.
В задачах типа TSP (как у тебя):
👉 важно не только какие города посещены, но и где мы сейчас
mask — какие города уже посещеныv — в каком городе мы сейчасСтартуем из города 1 (индекс 0):
cppdp[1 << 0][0] = 0;
То есть:
Мы пытаемся добавить новый город to:
cppif (!(mask & (1 << to))) // ещё не посещён
Переход:
Код:
cppint newMask = mask | (1 << to); dp[newMask][to] = min( dp[newMask][to], dp[mask][v] + a[v][to] );
cppfor (int mask = 0; mask < (1 << n); mask++) { for (int v = 0; v < n; v++) { if (!(mask & (1 << v))) continue; // v не в mask if (dp[mask][v] == INF) continue; for (int to = 0; to < n; to++) { if (mask & (1 << to)) continue; // уже был if (a[v][to] == 0) continue; // нет ребра int newMask = mask | (1 << to); dp[newMask][to] = min( dp[newMask][to], dp[mask][v] + a[v][to] ); } } }
Когда:
mask = (1 << n) - 1
это значит: посещены ВСЕ города
Берём минимум:
cppmin(dp[fullMask][v])
Храним родителей:
cppparMask[mask][v] parLast[mask][v]
И идём назад:
cppwhile (...) { path.push_back(v); ... }
Представь:
mask — какие города уже включеныv — где ты сейчас стоишьИ ты говоришь:
👉 “Если я сейчас в v, то могу пойти в to”
Потому что:
👉 значит, состояние обязано включать last
Для N ≤ 17 — нормально.
textгорода: 1,2,3 mask = 001 → {1} mask = 011 → {1,2} mask = 111 → {1,2,3}
DP строится так:
text{1} → {1,2} {1} → {1,3} {1,2} → {1,2,3} {1,3} → {1,2,3}
DP по подмаскам — это:
👉 перебор всех множеств
👉 хранение состояния “что уже выбрали”
👉 добавление одного элемента за шаг
Если хочешь, могу: