Компоненти зв'язності#2269

Компоненти зв'язності (Розбір)

Для знаходження кількості компонент зв'язності в графі можна використовувати систему неперетинних множин (Union-Find).

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

Цю задачу можна вирішити іншим способом — за допомогою пошуку в глибину. У цьому випадку кількість запусків процедури dfs співпадатиме з числом компонент зв'язності графа.

Приклад

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

Спочатку кожну вершину поміщаємо в окрему множину, де вона виступає представником.

Потім для кожного ребра об'єднуємо множини, що містять вершини і . Після обробки всіх ребер дві вершини належатимуть одній компоненті зв'язності, якщо у них співпадає представник.

Кількість компонент зв'язності збігається з числом множин у системі неперетинних множин. Це число визначається кількістю представників — тих вершин , для яких виконується умова parent[v] = v.

У наведеному прикладі представниками є вершини і . Отже, граф містить три компоненти зв'язності.

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

зберігає максимальну кількість вершин в графі. У масиві записаний номер вершини, на яку вказує вершина .

#define MAX 102
int mas[MAX];

Функція Repr повертає вершину-представника множини, до якого належить вершина . Для цього по вказівниках послідовно переходимо до наступного елемента, поки не дійдемо до представника множини (його вказівник вказує на нього самого).

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

Функція Union об'єднує дві множини, що містять вершини і . Спочатку знаходяться їх представники — нехай це будуть x_1 та y_1. Якщо x_1 = y_1, то вершини вже належать одній множині і додаткових дій не потрібно. В іншому випадку вказівник представника x_1 перенаправляється на y_1.

void Union(int x,int y)
{
  int x1 = Repr(x),y1 = Repr(y);
  if (x1 == y1) return;
  mas[x1] = y1;
}

Основна частина програми. Спочатку кожна вершина вказує сама на себе (mas[i] = i).

scanf("%d",&n);
for (i = 1; i <= n; i++) mas[i] = i;

Читаємо матрицю суміжності. Для кожного ребра , де i < j, виконуємо об'єднання множин, що містять вершини і .

for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
{
  scanf("%d",&value);
  if (i > j) continue;
  if (value) Union(i,j);
}

У змінній підраховуємо кількість компонент зв'язності. Це число співпадає з кількістю вершин-представників множин, тобто тих, які вказують самі на себе.

count = 0;
for (i = 1; i <= n; i++)
  if (mas[i] == i) count++;

Виводимо відповідь.

printf("%d\n",count);

Реалізація алгоритму – пошук в глибину

Оголосимо робочі масиви.

#define MAX 102
int g[MAX][MAX], used[MAX];

Функція dfs виконує обхід графа в глибину, починаючи з вершини .

void dfs(int v)
{
  used[v] = 1;
  for (int i = 0; i < n; i++)
    if (g[v][i] && !used[i]) dfs(i);
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d",&n);
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
  scanf("%d",&g[i][j]);

У змінній підраховуємо кількість компонент зв'язності.

res = 0;

Запускаємо пошук в глибину на незв'язному графі. Кожен запуск функції dfs починається з ще не відвіданої вершини, тому кількість викликів dfs збігається з числом компонент зв'язності графа.

memset(used,0,sizeof(used));
for (i = 0; i < n; i++)
  if (!used[i])
  {
    dfs(i);
    res++;
  }

Виводимо відповідь.

printf("%d\n",res);

Java реалізація – dfs

import java.util.*;

public class Main
{
  static int g[][], used[];
  static int n;
  
  static void dfs(int v)
  {
    used[v] = 1;
    for(int i = 1; i <= n; i++) 
      if (g[v][i] == 1 && used[i] == 0) dfs(i);
  }
  
  public static void main(String[] args)
  {
    Scanner con = new Scanner(System.in);
    n = con.nextInt();
    g = new int[n+1][n+1];
    used = new int[n+1];
    
    for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
      g[i][j] = con.nextInt();;

    int res = 0;
    for(int i = 1; i <= n; i++)
      if (used[i] == 0)
      {
        dfs(i);
        res++;
      }

    System.out.println(res);
    con.close();
  }
}   

Java реалізація – dsu

import java.util.*;

public class Main
{
  static int mas[];
  static int n;
  
  static int Repr(int n)
  {
    while (n != mas[n]) n = mas[n];
    return n;  
  }

  static void Union(int x,int y)
  {
    int x1 = Repr(x);
    int y1 = Repr(y);
    if (x1 == y1) return;
    mas[x1] = y1;
  }
  
  public static void main(String[] args)
  {
    Scanner con = new Scanner(System.in);
    n = con.nextInt();
    mas = new int[n+1];
    for(int i = 1; i <= n; i++) mas[i] = i;
    
    for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
    {
      int val = con.nextInt();
      if (i > j) continue;
      if (val == 1) Union(i,j);
    }
    
    int count = 0;
    for(int i = 1; i <= n; i++)
      if (mas[i] == i) count++;

    System.out.println(count);
    con.close();
  }
}   

Python реалізація – dsu

Функція Repr повертає вершину-представника множини, до якого належить вершина . Для цього по вказівникам послідовно переходимо до наступного елемента, поки не дійдемо до представника множини (його вказівник вказує на нього самого).

def Repr(n):
  while (n != mas[n]): n = mas[n]
  return n

Функція Union об'єднує дві множини, що містять вершини та . Спочатку знаходяться їх представники — нехай це будуть x_1 та y_1. Якщо x_1 = y_1, то вершини вже належать одній множині, і додаткових дій не потрібно. В іншому випадку вказівник представника x_1 перенаправляється на y_1.

def Union(x, y):
  x1 = Repr(x)
  y1 = Repr(y)
  if (x1 == y1): return
  mas[x1] = y1

Основна частина програми. Спочатку кожна вершина вказує сама на себе (mas[i] = i).

n = int(input())
mas = [int(i) for i in range (n + 1)]

Читаємо матрицю суміжності. Для кожного ребра , де i < j, виконуємо об'єднання множин, що містять вершини і .

for i in range(1,n + 1):
  lst = [0] + [int(j) for j in input().split()]
  for j in range(1, n + 1):
    if (i < j and lst[j] == 1): Union(i, j)

У змінній підраховуємо кількість компонент зв'язності. Це число співпадає з кількістю вершин-представників множин, тобто тих, які вказують самі на себе.

res = 0
for i in range(1, n + 1):
  if (mas[i] == i): res += 1

Виводимо відповідь.

print(res)

Python реалізація – dfs

Функція dfs виконує обхід графа в глибину, починаючи з вершини .

def dfs(v):
  used[v] = 1
  for i in range(n):
    if g[v][i] and not used[i]:
      dfs(i)

Основна частина програми. Читаємо вхідні дані.

n = int(input())
g = [[0] * n for _ in range(n)]
used = [0] * n
for i in range(n):
  row = list(map(int, input().split()))
  for j in range(n):
    g[i][j] = row[j]

У змінній підраховуємо кількість компонент зв'язності.

res = 0

Запускаємо пошук в глибину на незв'язному графі. Кожен запуск функції dfs починається з ще не відвіданої вершини, тому кількість викликів dfs збігається з числом компонент зв'язності графа.

for i in range(n):
  if not used[i]:
    dfs(i)
    res += 1

Виводимо відповідь.

print(res)