Побудуємо два графи: граф доріг і граф стежок . Далі обчислимо найкоротші відстані від села Домніно до всіх сіл:
За картою Стефана: — мінімальна відстань від до села ;
За картою Костянтина: — мінімальна відстань від до села ;
Потім розглянемо граф, вершинами якого є пари (v, parity):
— номер села;
parity = 0 (ніч, пересування по дорогах), parity = 1 (день, пересування по стежках).
Армія починає шлях увечері з початкового села, рухаючись вночі по дорозі (parity = 0). Запускаємо пошук в глибину з вершини за такими правилами:
з (ніч) йдемо по дорозі в , якщо d[0][u] < d[0][v];
з (день) йдемо по стежці в , якщо d[1][u] < d[1][v].
Перебуваючи в стані (v, parity), перебираємо лише допустимі переходи, тобто такі, при яких відстань до Домніно строго зменшується. Серед них шукаємо шлях максимальної довжини.
Нагадаємо, що кожен з гетьманів прагне рухатись так, щоб відстань за його картою зменшувалась (при цьому він не зобов’язаний слідувати найкоротшому шляху).
При виконанні пошуку в глибину будемо підтримувати таку динаміку:
dp[type][v] — максимальна довжина шляху від до , починаючи з типу .
Для кожного допустимого переходу:
Рекурсивно викликаємо dfs(to, type \oplus 1) — перехід в протилежну фазу.
Оновлюємо значення dp[type][v]:
Таким чином, ми обираємо найкращий наступний хід, який призводить до максимальної сумарної довжини шляху. Це аналог стандартного пошуку найдовшого шляху в DAG (орієнтованому ациклічному графі), де напрямок ребер визначається зменшенням найкоротшої відстані.
Рух дозволений лише по ребрах, що ведуть до вершин з меншою відстанню до Домніно. Однак у задачі присутні два графи – два типи ребер: дороги і стежки, – а переходи між ними чергуються: ніч (дорога), день (стежка), ніч, день і так далі. Саме через це чергування карт (доріг і стежок) може виникнути цикл між вершинами, навіть незважаючи на те, що в кожному окремому графі (за однією картою) рух завжди відбувається за вказаним напрямом.
Якщо при пошуку в глибину ми знову потрапляємо у вершину, яка вже знаходиться у стеку обробки, це означає, що виявлено цикл.
Реалізація алгоритму
Оголосимо константи.
#define INF 2e18
#define MAXN 1005Оголосимо використовувані структури даних:
і — списки суміжності для графів доріг і стежок відповідно.
і — найкоротші відстані від села Домніно до всіх інших сіл.
і — максимальна довжина шляху від вершини до , якщо стартувати з по дорозі (type = 0) або по стежці (type = 1).
і — мітки вершин при обході вглиб для графів доріг і стежок відповідно.
vector<pair<int, int>> g[2][MAXN];
long long d[2][MAXN];
long long dp[2][MAXN];
int used[2][MAXN];Функція dijkstra обчислює найкоротші відстані від села Домніно до всіх інших сіл або по дорогах (type = 0), або по стежках (type = 1).
void dijkstra(int type)
{
for (int i = 1; i <= n; i++)
d[type][i] = INF;
d[type][t] = 0;
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
pq.push({ 0, t });
while (!pq.empty())
{
pair<long long, int> top = pq.top();
pq.pop();
long long dist = top.first;
int v = top.second;
if (dist > d[type][v]) continue;
for (auto edge : g[type][v])
{
int to = edge.first;
int w = edge.second;
if (d[type][to] > d[type][v] + w)
{
d[type][to] = d[type][v] + w;
pq.push({ d[type][to], to });
}
}
}
}Функція dfs виконує пошук в глибину і знаходить найдовший шлях при русі по чергуючим картам, починаючи з села по дорозі (type = 0) або по стежці (type = 0). Якщо при такому чергуючому русі виникає можливість потрапити в цикл, функція повертає .
void dfs(int v, int type)
{Вершина в графі g[type] стає сірою.
used[type][v] = 1;Ініціалізуємо динаміку.
dp[type][v] = 0;Перебираємо ребра, що виходять з вершини в графі g[type].
for (auto edge : g[type][v])
{
int to = edge.first;
int weight = edge.second;Перехід з вершини у вершину можливий лише у випадку, якщо відстань від до Домніно менша, ніж відстань від до Домніно.
if (d[type][to] < d[type][v])
{Якщо вершина в графі g[type \oplus 1] вже знаходиться в процесі обходу (позначена як "сіра"), значить, виявлено цикл — повертаємо .
if (used[type ^ 1][to] == 1)
{
cout << -1 << endl;
exit(0);
}Якщо вершина в графі g[type \oplus 1] ще не відвідана, продовжуємо з неї пошук в глибину.
if (used[type ^ 1][to] == 0)
dfs(to, type ^ 1);Після обробки всіх допустимих переходів перераховуємо динаміку, обираючи максимальну довжину шляху від вершини в графі g[type] до Домніно.
dp[type][v] = max(dp[type][v], dp[type ^ 1][to] + weight);
}
}Вершина в графі g[type] позначається чорною.
used[type][v] = 2;
}Основна частина програми. Читаємо вхідні дані. Будуємо графи доріг і стежок.
scanf("%d %d %d", &n, &s, &t);
scanf("%d", &m1);
for (i = 0; i < m1; i++)
{
scanf("%d %d %d", &a, &b, &len);
g[0][a].push_back({ b, len });
g[0][b].push_back({ a, len });
}
scanf("%d", &m2);
for (i = 0; i < m2; i++)
{
scanf("%d %d %d", &a, &b, &len);
g[1][a].push_back({ b, len });
g[1][b].push_back({ a, len });
}Обчислюємо найкоротші відстані від села Домніно до всіх інших сіл — окремо для карти доріг і для карти стежок.
dijkstra(0);
dijkstra(1);За допомогою пошуку в глибину знаходимо найдовший шлях при русі по чергуючим картам, починаючи з села по дорозі.
dfs(s, 0);Виводимо відповідь. Стартова точка — село , рух починається по дорозі.
printf("%lld\n", dp[0][s]);