UVA - 1265 - Tour Belt 

This article will explain my thought process to solve a problem called Tour Belt (PDF) from UVA Online Judge. Read the original problem statement before reading this article.

Keywords: Graph Theory, Minimum Spanning Tree, Connected Components

Problem Summary 

Given:

  1. List of islands

  2. List of synergy effects between islands

We want to:

  1. Find total islands from every combinations of the islands that is eligible to be a tour belt

Glossary 

  1. CC — Connected Components

Observation 

The relationship between islands and its synergy effects can be modeled as a weighted graph, where the islands is the graph vertices, and the synergy effects is the weighted edges.

Tour belt is a subgraph where the minimum weight inside the subgraph is greater than the maximum weight on the boundaries of the subgraph.

Searching over every combinations of the vertices is impossible due to the large number of vertices. However, we could limit the search space by exploiting the tour belt structure. Since the inner weight must be greater than outer weight, we could reconstruct the graph starting from edges with the maximum weight to the minimum weight. At every iteration, any new CC that is formed has the potential to be a tour belt. We just need to validate the actual inner and border weight using the original graph.

Since we only care about new strongly connected components that can be formed with the maximum weight, this fits into the definition of a Minimum Spanning Tree. The search space now can be reduced into strongly connected components in a minimum spanning tree of maximum weight at every weight iteration.

Algorithm 

The minimum spanning tree of maximum weight can be build by sorting the edges by maximum weight first, then use a disjoint set to keep track of vertices that has been connected together. When we iterate the edges, any edge that contains a vertices that is not a part of the disjoint set is a minimum spanning tree edge.

After the minimum spanning tree is identified, we could start to rebuild the minimum spanning tree starting from edge with maximum weight. To find the CC, we use Disjoint Set and keeping track of the vertices inside each set for tour belt validation.

To validate the tour belt, we must rebuild the original graph by rebuilding the CC with original edges. To identify inner edges, find edges with both vertices included in the CC, and to identify border edges, find edges with one of the vertices included in the CC.

The time complexity for this solution is O({total vertices} ^ 2) or O(10^7).

Implementation 

import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 1265 - Tour Belt * Time limit: 3.000 seconds * https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3706 */ public class Main { public static void main(final String... args) { final Scanner in = new Scanner(new BufferedInputStream(System.in)); final PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out)); final Process process = new Process(); final int totalTestCases = in.nextInt(); for (int i = 0; i < totalTestCases; i++) { final Input input = new Input(); input.totalVertices = in.nextInt(); input.totalEdges = in.nextInt(); input.edges = new int[input.totalEdges][]; for (int j = 0; j < input.totalEdges; j++) { final int[] edge = new int[3]; edge[0] = in.nextInt(); edge[1] = in.nextInt(); edge[2] = in.nextInt(); input.edges[j] = edge; } final Output output = process.process(input); out.write(Integer.toString(output.totalCandidateVertices)); out.write('\n'); } in.close(); out.flush(); out.close(); } } class Input { public int totalVertices; public int totalEdges; public int[][] edges; } class Output { public int totalCandidateVertices; } class Process { public Output process(final Input input) { final Output output = new Output(); final List<Edge> edges = getEdges(input); final List<Edge> minimumSpanningTreeEdges = getMinimumSpanningTreeOfMaximumWeight(edges); final DisjointSet<Integer> ccSet = new DisjointSet<>(); final Map<Integer, Set<Integer>> verticesPerCC = new HashMap<>(); // loop edge from the greatest weight // this will create subgraph consisting of greatest edge at every iteration for (final Edge edge : minimumSpanningTreeEdges) { // find connected components of vertex1 and vertex2 final int cc1 = ccSet.find(edge.vertex1); final int cc2 = ccSet.find(edge.vertex2); final Set<Integer> vertices1 = verticesPerCC.getOrDefault(cc1, Collections.singleton(cc1)); final Set<Integer> vertices2 = verticesPerCC.getOrDefault(cc2, Collections.singleton(cc2)); // combine connected components of vertex1 and vertex2 ccSet.union(edge.vertex1, edge.vertex2); final int cc = ccSet.find(cc1); final Set<Integer> vertices = Stream .concat(vertices1.stream(), vertices2.stream()) .collect(Collectors.toSet()); verticesPerCC.put(cc, vertices); // validate constraint if (isValidSubGraph(edges, vertices)) { output.totalCandidateVertices += vertices.size(); } } return output; } private List<Edge> getEdges(final Input input) { return Arrays.stream(input.edges) .map(edge -> new Edge(edge[0], edge[1], edge[2])) .collect(Collectors.toList()); } private List<Edge> getMinimumSpanningTreeOfMaximumWeight(final List<Edge> edges) { edges.sort(Edge.ORDER_BY_WEIGHT_DESC); final List<Edge> minimumSpanningTree = new LinkedList<>(); final DisjointSet<Integer> set = new DisjointSet<>(); for (final Edge edge : edges) { final int parent1 = set.find(edge.vertex1); final int parent2 = set.find(edge.vertex2); if (parent1 != parent2) { minimumSpanningTree.add(edge); set.union(edge.vertex1, edge.vertex2); } } return minimumSpanningTree; } private boolean isValidSubGraph(final List<Edge> edges, final Set<Integer> vertices) { final int minimumInnerWeight = getMinimumInnerWeight(edges, vertices); final int maximumBorderWeight = getMaximumBorderWeight(edges, vertices); return minimumInnerWeight > maximumBorderWeight; } private int getMinimumInnerWeight(final List<Edge> edges, final Set<Integer> vertices) { return edges.stream() .filter(e -> vertices.contains(e.vertex1) & vertices.contains(e.vertex2)) .mapToInt(e -> e.weight) .min() .orElse(Integer.MAX_VALUE); } private int getMaximumBorderWeight(final List<Edge> edges, final Set<Integer> vertices) { return edges.stream() .filter(e -> vertices.contains(e.vertex1) ^ vertices.contains(e.vertex2)) .mapToInt(e -> e.weight) .max() .orElse(Integer.MIN_VALUE); } } class Edge { public static final Comparator<Edge> ORDER_BY_WEIGHT_DESC = Comparator.comparingInt(e -> -e.weight); public final int vertex1; public final int vertex2; public final int weight; public Edge(final int vertex1, final int vertex2, final int weight) { this.vertex1 = vertex1; this.vertex2 = vertex2; this.weight = weight; } } class DisjointSet<V> { private final Map<V, V> parentMap = new HashMap<>(); public V find(final V child) { final V parent = parentMap.getOrDefault(child, child); if (parent == child) { return parent; } else { final V grandparent = find(parent); parentMap.put(child, grandparent); return grandparent; } } public void union(final V child1, final V child2) { final V parent1 = find(child1); final V parent2 = find(child2); parentMap.put(parent2, parent1); } }


Powered by Docmost