Вот подход O (n * 2 ^ n) динамического программирования, который должен быть выполнимым, скажем, до 20 вершин:
m(b, U)
= максимальная длина любого пути, заканчивающегося на b
и посещающего только (некоторые из) вершин в U
.
Первоначально установите m(b, {b}) = 0
.
Тогда m(b, U)
= максимальное значение m(x, U - x) + d(x, b)
для всех x
в U
, так что x
не является b
и существует ребро (x, b)
. Возьмите максимум этих значений для всех конечных точек b
, с U
= V
(полный набор вершин). Это будет максимальная длина любого пути.
В следующем коде C предполагается матрица расстояний в d[N][N]
. Если ваш график невзвешенный, вы можете изменить каждый доступ на чтение к этому массиву на константу 1
. В массиве p[N][NBITS]
.
также вычисляется трассировка, показывающая оптимальную последовательность вершин (их может быть больше одной).
#define N 20
#define NBITS (1 << N)
int d[N][N]; /* Assumed to be populated earlier. -1 means "no edge". */
int m[N][NBITS]; /* DP matrix. -2 means "unknown". */
int p[N][NBITS]; /* DP predecessor traceback matrix. */
/* Maximum distance for a path ending at vertex b, visiting only
vertices in visited. */
int subsolve(int b, unsigned visited) {
if (visited == (1 << b)) {
/* A single vertex */
p[b][visited] = -1;
return 0;
}
if (m[b][visited] == -2) {
/* Haven't solved this subproblem yet */
int best = -1, bestPred = -1;
unsigned i;
for (i = 0; i < N; ++i) {
if (i != b && ((visited >> i) & 1) && d[i][b] != -1) {
int x = subsolve(i, visited & ~(1 << b));
if (x != -1) {
x += d[i][b];
if (x > best) {
best = x;
bestPred = i;
}
}
}
}
m[b][visited] = best;
p[b][visited] = bestPred;
}
return m[b][visited];
}
/* Maximum path length for d[][].
n must be <= N.
*last will contain the last vertex in the path; use p[][] to trace back. */
int solve(int n, int *last) {
int b, i;
int best = -1;
/* Need to blank the DP and predecessor matrices */
for (b = 0; b < N; ++b) {
for (i = 0; i < NBITS; ++i) {
m[b][i] = -2;
p[b][i] = -2;
}
}
for (b = 0; b < n; ++b) {
int x = subsolve(b, (1 << n) - 1);
if (x > best) {
best = x;
*last = b;
}
}
return best;
}
На моем ПК это решает полный граф 20x20 с весами ребер, случайно выбранными в диапазоне [0, 1000), примерно за 7 с и требует около 160 Мб (половина этого для трассы предшественника).
(Пожалуйста, без комментариев по поводу использования массивов фиксированного размера. Используйте malloc()
(или еще лучше, C ++ vector<int>
) в реальной программе. Я просто написал это таким образом, чтобы все было яснее.)