1 of 27

CP2: Topic 8: Union Find and Minimal Spanning Tree

Competitive Programming II (Spring 2020)

Ninghui Li

2 of 27

Union Find (aka Disjoint-Set)

Data Structure

3 of 27

Union-Find (aka Disjoint-Set) Data Structure

  • Keep tracks of a set of elements partitioned into a number of disjoint sets that can be merged.
  • Supports the following operations efficiently (almost constant time asymptotically)
    • find(a) returns the id of the set a is in.
      • a and b are in the same set if and only if find(a)=find(b)
    • union(a, b) joins the set that a is in with the set that b is in

4 of 27

Implementation of Union-Find

  • Each item is identified by an integer index value.
  • All items form a forest (multiple trees)
  • Uses an array that stores the parent of each node.
  • If a node is root of a tree, its parent is itself.
  • Find(a) returns the root of the tree a is in.
  • Union(a, b) joins together the trees a and b are in by finding both and setting one root as the parent of the other.

5 of 27

Union-Find: Strategy for Unions for Efficiency

  • Union by rank
    • Balances union-find trees so that the height, or rank, of the tree formed in a union is as low as possible.
  • Union by size
    • Make the root of the bigger tree the root of the merged tree

6 of 27

Union By Size Example

0

1

2

3

8

7

4

5

6

+

=

0

1

2

3

4

5

8

7

6

0

1

2

3

4

5

6

7

8

0

0

0

1

2

2

6

6

6

0

1

2

3

4

5

6

7

8

0

0

0

1

2

2

0

6

6

Union(4, 7);

7 of 27

Union-Find: Path Compression

  • Path compression flattens the path from a node being found to the root of its tree.
  • This can be done by finding the node and setting all nodes on the path to the root to have the root as their parent.
  • The next time one of the affected nodes is found, it will be constant time.
  • This combined with union by size gives union-find a near-constant time complexity.

8 of 27

Path Compression Example

0

1

2

3

4

5

8

7

6

9

0

1

2

3

4

5

8

7

6

9

0

1

2

3

4

5

6

7

8

9

0

0

0

1

2

2

3

3

4

5

0

1

2

3

4

5

6

7

8

9

0

0

0

1

0

2

3

3

0

5

Find(8);

9 of 27

Union Find Code in Java (Union by size)

class UF {

int[] parent, size; // parent[i] = parent of i

int count; // number of components

public UF(int n) {

count = n; parent = new int[n]; size = new int[n];

for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; }

}

public int find(int p) {

while (p!=parent[p]) { parent[p]=parent[parent[p]]; p=parent[p]; }

return p;

}

public void union(int p, int q) {

int a = find(p), b = find(q);

if (a == b) return;

if (size[a] <= size[b]) { parent[a]=b; size[b]+= size[a]; }

else { parent[b] = a; size[a]+=size[b]; }

count--;

}

}

10 of 27

Minimal Spanning Tree

11 of 27

Minimum Spanning Tree

  • Given a weighted graph, find the a minimum weight set of edges that connects all the nodes in the tree.
  • There are two efficient algorithms to find the MST: Prim’s Algorithm and Kruskal’s Algorithm.

4

2

5

7

10

3

6

1

8

9

5

2

4

2

7

3

6

1

5

2

MST

12 of 27

Kruskal’s Algorithm

  • Sort the a list of edges by increasing weight.
  • Remove an edge from the list. If the edge connects two nodes that aren’t in the same tree, add it to the MST.
  • Use union find to test if two nodes are in the same tree.
  • Repeat until all nodes are in the tree.
  • O(e*log(n))

13 of 27

Prim’s Algorithm

  • Maintain a discovered portion
  • Start from one node in discovered portion and maintain distance of other nodes to discovered portion
  • Each time adding a node with smallest distance to discovered portion
  • Naive Implementation has complexity O(N^2)
  • Can use PriorityQueue to get complexity O(E log V)

14 of 27

Prim’s Algorithm

15 of 27

Minimal Spanning Tree: Example 1

Kattis: A Feast for Cats

16 of 27

Given M ml of milk, and C cats – each pair of cats being a given distance apart from each other – determine if it’s possible to feed every cat at least 1 ml of milk. You will spill 1 ml of the milk you carry for every meter you walk. Instead of carrying all the milk at once, you may serve a cat more milk than needed and pick it up when backtracking (the cat will not drink the excess milk before you’re finished walking). The fridge (which has milk) is placed at the very same location as cat 0.

  • 1 ≤ M ≤ 20000 1 ≤ C ≤ 2000 1 ≤ Di,j ≤ 3000
  • Cats co-exist in dimensions far greater than our three, so you can assume that every pair of cats is correctly separated by exactly the given distance.
  • (In other words, this is just a graph, pretending to be cats who want milk.)
  • How to solve this?

17 of 27

A Feast for Cats asks for a Minimal Spanning Tree (MST)

  • A minimal spanning tree is a tree that connects all nodes where the sum of distances is minimal
  • Because one can leave milk at a node and pick up later, the total spilled amount is exactly the total weight of Minimal Spanning Tree

18 of 27

Implementation of Kruskal’s Algorithm for Cats

int[][] edges; // edges[i]:{weight, u, v}

Arrays.sort(edges, 0, E, comp);

UF nodes = new UF(N);

int mst_cost = 0, k = 0;

while (nodes.count > 1) { // graph is fully connected

int u = edges[k][1], v = edges[k][2];

if (nodes.find(u) != nodes.find(v)) {

mst_cost += edges[k][0];

nodes.union(u, v);

}

k++;

}

19 of 27

Minimal Spanning Tree: Example 2

Kattis: Nature Reserve

20 of 27

Kattis: Nature Reserve

Give N stations, where S of them already have a program of L bytes, find out the minimal amount of energy needed to have all stations install the program. There are M links among pairs of stations. Activating a link between stations i and j costs energy E_{ij}. Transmitting one byte costs 1 unit of energy.

1 ≤ S ≤ N ≤ 10^4 1 ≤ M ≤ min(10^6, N(N-1)/2) 1 ≤ L ≤ 10^6

21 of 27

Kattis: Nature Reserve Analysis

  • Total amount of transmission cost is fixed: (N-S)*L
  • We want to minimize the activation cost
  • We need to connect every node with one of the sources
  • Use Prim’s algorithm, with multiple sources to start, stop until all nodes are connected

22 of 27

Java Code for Nature Reserve

int connected = 0; // number of connected nodes

int[] cs = new int[N]; // current best cost for node

for (int i=0; i<N; i++) cs[i] = LARGE_INT;

long cost = ((long) L) * (N - S); // cost of transmission

while (connected < N) { //

int[] eg = pq.poll(); // pq initialized with S source, with 0

int u = eg[1];

if (cs[u] != 0) { // node already connected has cs[u]==0

cost += eg[0]; cs[u] = 0; connected ++;

for (int[] edg : adj.get(u)) {

int v = edg[1], w = edg[0];

if (cs[v] > w) { pq.add(edg); cs[v] = w; }

}

}

}

23 of 27

24 of 27

Kattis: Association for Control over Minds

There are at most 500001 types of items, and you have one item of each type. You have 2≤N≤200000 recipes for potions, each consisting of some item types. You go through the recipes one by one and use cauldrons to make potions. You can make a potion if you can combine existing potions and unused items to get exactly the same set of items as in the recipe. You will make it when you can, and skip a recipe when you cannot. Computer the number of potions you can make.

Input: 5 2 1 2 2 3 4 2 1 5 5 1 2 3 4 5 2 1 2

Output: 3

25 of 27

Association for Control over Minds: Analysis

  • Given a new recipe, we need to determine whether it can be made.
  • For each item on the recipe, we want to know which cauldron it is in, and check whether combining these cauldrons can result in exactly the set.
  • We can use Union Find. Each each item, find the group it is in. Find all unique groups that the items are in, add up their size. If the size is the same as number of items in the recipe, then we can make it.

26 of 27

Java Code for Mind Control

UF items = new UF(500001);

int N = in.nextInt(), ans = 0;

for (int i=0; i<N; i++) { // loop over all recipes

int M = in.nextInt(), size = 0;

HashSet<Integer> cauld = new HashSet<Integer>();

for (int j=0; j<M; j++) {

int x = in.nextInt(), y = items.find(x);

if (cauld.contains(y)) continue;

cauld.add(y); size += items.size[y];

}

if (size != M) continue;

ans++; int y = -1;

for (int x : cauld) if (y==-1) y = x;

else items.union(x,y);

}

27 of 27

Hints for Homework

  • Kattis: Lost Map
    • Interesting to figure out why this is essentially an MST problem
  • Kattis: Millionaire Madness
    • Implicit graph, several different solutions exist
    • Bisection + connectivity (DFS with explicit stack, BFS, UF)
    • Sorting edge + UF [This may not work due to directed nature of the graph]
    • Use Prim’s algorithm: start from entrance, and stop when vault is added, the weight of max edge is the answer.
  • Kattis: Ladice
    • Can be solved using modified Union Find.