Подорож#2267

Подорож (Розбір)

Побудуємо два графи: граф доріг і граф стежок . Далі обчислимо найкоротші відстані від села Домніно до всіх сіл:

Потім розглянемо граф, вершинами якого є пари (v, parity):

Армія починає шлях увечері з початкового села, рухаючись вночі по дорозі (parity = 0). Запускаємо пошук в глибину з вершини за такими правилами:

Перебуваючи в стані (v, parity), перебираємо лише допустимі переходи, тобто такі, при яких відстань до Домніно строго зменшується. Серед них шукаємо шлях максимальної довжини.

Нагадаємо, що кожен з гетьманів прагне рухатись так, щоб відстань за його картою зменшувалась (при цьому він не зобов’язаний слідувати найкоротшому шляху).

При виконанні пошуку в глибину будемо підтримувати таку динаміку:

Для кожного допустимого переходу:

dp[type][v] = max(dp[type][v], dp[type \oplus 1][to] + weight);

Таким чином, ми обираємо найкращий наступний хід, який призводить до максимальної сумарної довжини шляху. Це аналог стандартного пошуку найдовшого шляху в DAG (орієнтованому ациклічному графі), де напрямок ребер визначається зменшенням найкоротшої відстані.

Рух дозволений лише по ребрах, що ведуть до вершин з меншою відстанню до Домніно. Однак у задачі присутні два графи – два типи ребер: дороги і стежки, – а переходи між ними чергуються: ніч (дорога), день (стежка), ніч, день і так далі. Саме через це чергування карт (доріг і стежок) може виникнути цикл між вершинами, навіть незважаючи на те, що в кожному окремому графі (за однією картою) рух завжди відбувається за вказаним напрямом.

Якщо при пошуку в глибину ми знову потрапляємо у вершину, яка вже знаходиться у стеку обробки, це означає, що виявлено цикл.

Реалізація алгоритму

Оголосимо константи.

#define INF 2e18
#define MAXN 1005

Оголосимо використовувані структури даних:

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]);