Дорога#6576

Дорога (Розбір)

Запустим алгоритм Крускала на вихідному графі. В множину додамо ребра, які входять в мінімальне остовне дерево. Далі перевіримо, чи належить задане ребро цьому остову. Для цього необхідно перевірити, чи міститься пара або в множині .

Приклад

Графи, наведені в прикладах, мають наступний вигляд:

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

Оголосимо структуру ребра графа, що містить пару вершин і вагу ребра. Оголосимо масив ребер .

struct Edge
{
  int u, v, dist;
};

vector<Edge> e;

Оголосимо масив , що використовується для реалізації структури неперетинальних множин.

vector<int> parent;

Функція Repr знаходить представника множини, якому належить вершина .

int Repr(int n)
{
  while(n != parent[n]) n = parent[n];
  return n;
}

Функція Union об'єднує множини, що містять елементи і .

int Union(int x, int y)
{
  x = Repr(x); y = Repr(y);
  if (x == y) return 0;
  parent[y] = x;
  return 1;
}

Функція lt служить компаратором для сортування ребер.

int lt(Edge a, Edge b)
{
  return (a.dist < b.dist);
}

Основна частина програми. Обробляємо декілька тестів.

scanf("%d", &tests);
while (tests--)
{

Читаємо вхідні дані. Ініціалізуємо масив .

  scanf("%d %d %d %d", &n, &m, &p, &q);
  parent.resize(n + 1);
  for (i = 1; i <= n; i++) parent[i] = i;

Читаємо ребра графа.

  e.resize(m);
  for (i = 0; i < m; i++)
    scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].dist);

Сортуємо ребра графа за зростанням ваги.

  sort(e.begin(), e.end(), lt);

При обробці кожного тесту обнуляємо змінну . Вона приймає значення або залежно від того, чи належить ребро мінімальному остовному дереву.

  flag = 0;

Запускаємо алгоритм Крускала для побудови мінімального остову. Якщо ребро (e[i].u, e[i].v) включається в мінімальний остов, перевіряємо, чи співпадає воно з ребром .

  for (i = 0; i < m; i++)

    if (Union(e[i].u, e[i].v))

Необхідно перевірити дві умови: (e[i].u, e[i].v) = (p, q) і (e[i].u, e[i].v) = (q, p).

      if ((e[i].u == p && e[i].v == q) || 
          (e[i].u == q && e[i].v == p))
      {

Якщо ребро (e[i].u, e[i].v) співпадає з або , встановлюємо flag = 1 і виходимо з циклу.

        flag = 1;
        break;
      }

Залежно від значення змінної виводимо відповідь.

  if (flag)
    puts("YES");
  else
    puts("NO");
}

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

Оголосимо структуру ребра графа, що містить пару вершин і вагу ребра. Оголосимо масив ребер .

struct Edge
{
  int u, v, dist;
};

vector<Edge> e;

Оголосимо масив , що використовується для реалізації структури неперетинальних множин.

vector<int> parent;

У множині буде зберігатися ребра, що входять у мінімальне остовне дерево.

set<pair<int, int> > s;

Функція Repr знаходить представника множини, якому належить вершина .

int Repr(int n)
{
  while(n != parent[n]) n = parent[n];
  return n;
}

Функція Union об'єднує множини, що містять елементи і .

int Union(int x, int y)
{
  x = Repr(x); y = Repr(y);
  if (x == y) return 0;
  parent[y] = x;
  return 1;
}

Функція lt служить компаратором для сортування ребер.

int lt(Edge a, Edge b)
{
  return (a.dist < b.dist);
}

Основна частина програми. Обробляємо декілька тестів.

scanf("%d", &tests);
while (tests--)
{

Читаємо вхідні дані. Ініціалізуємо масив .

  scanf("%d %d %d %d", &n, &m, &p, &q);
  parent.resize(n + 1);
  for (i = 1; i <= n; i++) parent[i] = i;

Читаємо ребра графа.

  e.resize(m);
  for (i = 0; i < m; i++)
    scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].dist);

Сортуємо ребра графа за зростанням ваги.

  sort(e.begin(), e.end(), lt);

При обробці кожного тесту очищаємо множину .

  s.clear();

Запускаємо алгоритм Крускала для побудови мінімального остовного дерева. Якщо ребро (e[i].u, e[i].v) включається в остов, додаємо його в множину .

  for (i = 0; i < m; i++)
    if (Union(e[i].u, e[i].v)) s.insert(make_pair(e[i].u, e[i].v));

Після завершення алгоритму перевіряємо, чи належить задане ребро мінімальному остову. Для цього потрібно з'ясувати, чи міститься пара або в множині .

  if ((s.find(make_pair(p, q)) != s.end()) ||
      (s.find(make_pair(q, p)) != s.end()))
    puts("YES");
  else
    puts("NO");
}

Java реалізація

import java.util.*;

class Edge
{
  int u, v, dist;
  Edge (int u, int v, int dist)
  {
    this.u = u;
    this.v = v;
    this.dist = dist;
  }
};

class Pair
{
  int u, v;
  Pair (int u, int v)
  {
    this.u = u;
    this.v = v;
  }
};

public class Main
{
  static int mas[];
  static int size[];
	
  static int Repr(int n)
  {
    if (n == mas[n]) return n;
    return mas[n] = Repr(mas[n]);
  }
	
  static int Union(int x, int y)
  {
    x = Repr(x); y = Repr(y);
    if(x == y) return 0;

    if (size[x] < size[y])
    {
      int temp = x; x = y; y = temp;
    }
    mas[y] = x;
    size[x] += size[y];
    return 1;
  }
  
  public static class MyFun implements Comparator<Edge>
  {
    public int compare(Edge a, Edge b)
    {
      return a.dist - b.dist;
    }
  }

  public static class PairFun implements Comparator<Pair>
  {
    public int compare(Pair a, Pair b)
    {
      if (a.u == b.u) return a.v - b.v;
      return a.u - b.u;
    }
  }
  
  public static void main(String[] args)
  {
    Scanner con = new Scanner(System.in);
    int tests = con.nextInt();
    while(tests-- > 0)
    {
      int n = con.nextInt();
      int m = con.nextInt();
      int p = con.nextInt();
      int q = con.nextInt();
      
      mas = new int[n+1];
      size = new int[n+1];
      for(int i = 1; i <= n; i++)
      {
        mas[i] = i;
        size[i] = 1;
      }

      Vector<Edge> v = new Vector<Edge>();
      for(int i = 0; i < m; i++)
      {
        int x = con.nextInt();
        int y = con.nextInt();
        int dist = con.nextInt();
        v.add(new Edge(x,y,dist));
      }

      Collections.sort(v, new MyFun());
    
      TreeSet<Pair> s = new TreeSet<Pair>(new PairFun());
      for(int i = 0; i < m; i++)
        if (Union(v.get(i).u,v.get(i).v) == 1) s.add(new Pair(v.get(i).u,v.get(i).v));
    
      if(s.contains(new Pair(p,q)) || s.contains(new Pair(q,p)))
        System.out.println("YES");
      else 
        System.out.println("NO");
    }
    con.close();
  }
}