Download presentation
Presentation is loading. Please wait.
Published byMarvin Cain Modified over 8 years ago
1
Honors Track: Competitive Programming & Problem Solving Fun with Graphs I Kevin Verbeek
2
Graph algorithms Standard Algorithms DFS BFS Single source shortest path All-pairs shortest path Minimum spanning tree Euler tour Bipartite matching Max-flow Fun with Graphs I Fun with Graphs II
3
Graph representation Graph representations Adjacency matrix ➨ easy, but slow and memory-heavy Adjacency list ➨ straightforward and efficient C++Java struct Edge { int target; int weight; … }; struct Node { vector adj; bool visited; … }; Node V[10005]; class Edge { int target; int weight; … } class Node { ArrayList adj; boolean visited; … } Node[] V;
4
Graph Input Reading Input void main() { int N = sc.nextInt(); V = new Node[N]; // in C++ initialize visited and clear adj // read per-vertex input if applicable… int M = sc.nextInt(); for (int i = 0; i < M; i++) { // reading edges int a = sc.nextInt(); int b = sc.nextInt(); int w = sc.nextInt(); // if weighted a--; b--; // if input is 1-based instead of 0-based V[a].adj.add(new Edge(b, w)); V[b].adj.add(new Edge(a, w)); // only if undirected } // do something with graph }
5
Depth First Search DFS Easy to implement with recursive function Running time is O(V + E) void dfs(int i) { if (V[i].visited) return; V[i].visited = true; for (Edge e: V[i].adj) { dfs(e.target); } } Problems Flood fill (easy) Connected components (easy) Strongly connected components (Tarjan’s algorithm) Biconnected components
6
Flood Fill Find connected area in a grid Start from given set of seeds Run DFS for each seed Can use “visited” to keep track Graphs Also works for graphs in general “Find nodes connected to seeds” class Node { ArrayList adj; boolean visited; boolean obs; }
7
Flood Fill int[] dx = {1, 0, -1, 0}; int[] dy = {0, 1, 0, -1}; Node[][] V; void dfs(int x, int y) { if (V[x][y].obs || V[x][y].visited) return; V[x][y].visited = true; for (int i = 0; i < 4; i++) { dfs(x + dx[i], y + dy[i]); } void main() { ArrayList seeds = … // read input… for (Pair p: seeds) { dfs(p.x, p.y); } // do something with visited nodes }
8
Flood Fill For Graphs Node[] V; void dfs(int i) { if (V[i].obs || V[i].visited) return; V[i].visited = true; for (Edge e: V[i].adj) { dfs(e.target); } void main() { ArrayList seeds = … // read input… for (Integer i: seeds) { dfs(i); } // do something with visited nodes }
9
Connected Components Find number of disconnected parts Nodes of same part get same number Similar to Flood Fill Common part of problems (2 in EAPC!) Dynamic version Edges are added dynamically Use Union-find class Node { ArrayList adj; boolean visited; int comp; // can use -1 to replace visited }
10
Connected Components Node[] V; void dfs(int i, int c) { if (V[i].visited) return; V[i].visited = true; V[i].comp = c; for (Edge e: V[i].adj) { dfs(e.target, c); } void main() { V = new Node[N]; // read input… int C = 0; // C is the number of connected components for (int i = 0; i < N; i++) { if (!V[i].visited) dfs(i, C++); } // do something with connected components }
11
A B C D E F Strongly Connected Components Only for directed graphs u and v in same SCC if u can reach v and v can reach u This is a partition of the nodes of a directed graph Reduced graph on SCCs is a DAG A B C D EF
12
Tarjan’s algorithm Perform a DFS Assign number to each node based on DFS order For each node v keep track of lowest index node reachable from v Keep stack of vertices not in SCC yet If v.index = v.low then all nodes on stack until v are SCC Running time O(V + E) A B C D E F A B C D EF
13
Tarjan’s algorithm Stack 0 4 1 3 6 7 5 11 2 0 12 3 4 5 6 7 8 9 10 11 13 8 9 10 12 13
14
Tarjan implementation Keep track of current index Must know if node is on stack in O(1) time (!) Compute component index for each node class Node { ArrayList adj; int index; // replaces visited, initially -1 int low; int comp; // initially -1, can be used to check if on stack } int index = 0, C = 0; ArrayDeque stack = new ArrayDeque ();
15
Tarjan implementation void tarjan(int i) { // run for every i with V[i].index == -1 V[i].index = index++; V[i].low = V[i].index; stack.push(i); for (Edge e: V[i].adj) { if (V[e.target].index == -1) { tarjan(e.target); V[i].low = Math.min(V[i].low, V[e.target].low); } else if (V[e.target].comp == -1) { // e.target must be on stack V[i].low = Math.min(V[i].low, V[e.target].index); } if (V[i].index == V[i].low) { // SCC is found int j; do { j = stack.pop(); V[j].comp = C; } while(j != i); C++; }
16
Strongly Connected Components Problem How many edges are needed to make graph strongly connected? Solution Compute strongly connected components Compute reduced graph (contract SCCs) If #SCCs is 1, then return 0 Else return max(X, Y) X is the number of sources (indegree 0) Y is the number of sinks (outdegree 0) Actually computing edges is much harder Reduced graph Make node for each SCC For all edges (u, v) add edge if SCC of u and v are different Remove duplicates if necessary (not for this problem) A B C D EF
17
Biconnected Components Defined for undirected graphs Subgraph is biconnected if two nodes must be removed to disconnect the subgraph Biconnected component is subset of edges Nodes in 2 BCs are cut/articulation nodes Reduced graph is a tree Algorithm similar to Tarjan’s 2-edge-connected Subgraph is 2-edge-connected if two edges must be removed to disconnect the subgraph 2-edge-connected component is subset of nodes
18
Shortest Paths Single source shortest paths Also for the shortest path between two points A common problem during contests Do the edges have different lengths/weights? Can the edges have negative weight? No (for example for grids) Yes No BFS Dijkstra Bellman-Ford
19
Breadth First Search BFS Slightly harder to implement than DFS Useful for simple shortest path problems Easy to implement with a queue Running time is O(V + E) Grid-BFS These problems often occur Almost always solvable using a BFS You sometimes need to be creative with states Hint: For Grid-BFS, add a wall around the grid ## #### # # # # ### # # p# # #######
20
Graph BFS class Node { ArrayList adj; int dist; // dist = -1 if not visited, initially -1 (must be set!) } void main() { // read input… including source(s) ArrayList queue = new ArrayList (); // can use ArrayDeque V[source].dist = 0; queue.add(source); for (int i = 0; i < queue.size(); i++) { int k = queue[i]; for (Integer j: V[k].adj) { if (V[j].dist >= 0) continue; // node already found V[j].dist = V[k].dist + 1; queue.add(j); }
21
State Search BFS for States BFS works well for state space searching “How many steps to get from one state to another?” Number of states depends on state space Example: Cannibals and Missionaries Three cannibals and three missionaries come to a crocodile infested river. There is a boat on their side that can be used by either one or two persons. If cannibals outnumber the missionaries at any time, the cannibals eat the missionaries. How can they use the boat to cross the river so that all missionaries survive?
22
State Search Requirements State representation (CanLeft, MisLeft, BoatPos), 0 ≤ CanLeft/MisLeft ≤ 3, BoatPos = 0/1 State transitions Put 1 or 2 people on boat Valid states #cannibals cannot outnumber #missionaries on either side Initial state CanLeft = MisLeft = 3 and BoatPos = 0 End state(s) CanLeft = MisLeft = 0
23
States General Setup class State { int canLeft, misLeft, boatPos; State(int c, int m, int b) {…}; boolean isValid() {…}; boolean isEnd() {…}; ArrayList trans() {…}; } void main() { ArrayList queue = new ArrayList (); // can use ArrayDeque queue.add(initState); for (int i = 0; i < queue.size(); i++) { if (queue[i].isEnd()) {…}; // finish the BFS (could be done earlier…) ArrayList L = queue[i].trans(); for (State S: L) { if (!S.isValid() || visited[S]) continue; queue.add(S); }
24
State Checks class State { int canLeft, misLeft, boatPos; State(int c, int m, int b) {…}; boolean isValid() { if (misLeft > 0 && canLeft > misLeft) return false; if (misLeft < 3 && canLeft < misLeft) return false; return true; } boolean isEnd() {…}; ArrayList trans() {…}; } class State { int canLeft, misLeft, boatPos; State(int c, int m, int b) {…}; boolean isValid() {…}; boolean isEnd() { return (misLeft + canLeft == 0); } ArrayList trans() {…}; }
25
State Transitions class State { int canLeft, misLeft, boatPos; State(int c, int m, int b) {…}; boolean isValid() {…}; boolean isEnd() {…}; ArrayList trans() { ArrayList L = new ArrayList (); if (boatPos == 0) { // can be different by changing isValid… if (canLeft > 0) L.add(new State(canLeft-1, misLeft, 1-boatPos)); if (misLeft > 0) L.add(new State(canLeft, misLeft-1, 1-boatPos)); if (canLeft > 1) L.add(new State(canLeft-2, misLeft, 1-boatPos)); if (misLeft > 1) L.add(new State(canLeft, misLeft-2, 1-boatPos)); if (canLeft > 0 && misLeft > 0) L.add(new State(canLeft-1, misLeft-1, 1-boatPos)); } else { … } return L; }
26
State Visited How to check if State has already been visited? Method 1 Keep multidimensional boolean array Array is indexed by state variables Can store extra info in State class (distance, parent, etc.) Possible states not too large and indexable Method 2 Keep HashSet of visited States Simple and can hold arbitrary States Cannot store extra info in State class (or override hashcode()) Slight computational overhead
27
State Visited Method 1 class State { int canLeft, misLeft, boatPos; int dist; // extra information can be stored and used State parent; // extra information updated in trans() … } … boolean[][][] visited = new boolean[4][4][2]; visited[3][3][0] = true; initState = new State(3, 3, 0, 0, null); queue.add(initState); for (int i = 0; i < queue.size(); i++) { if (queue[i].isEnd()) {…}; // finish the BFS (could be done earlier…) ArrayList L = queue[i].trans(); for (State S: L) { if (!S.isValid() || visited[S.canLeft][S.misLeft][S.boatPos]) continue; visited[S.canLeft][S.misLeft][S.boatPos] = true; queue.add(S); }
28
State Visited Method 2 class State { int canLeft, misLeft, boatPos; … } … HashSet visited = new HashSet (); initState = new State(3, 3, 0); visited.add(initState); queue.add(initState); for (int i = 0; i < queue.size(); i++) { if (queue[i].isEnd()) {…}; // finish the BFS (could be done earlier…) ArrayList L = queue[i].trans(); for (State S: L) { if (!S.isValid() || visited.contains(S)]) continue; visited.add(S); queue.add(S); }
29
State Visited Method 2 (continued) class StateInfo { int dist; State parent; … } … HashMap info = new HashMap (); initState = new State(3, 3, 0); info.put(initState, new StateInfo(0, null)); … for (int i = 0; i < queue.size(); i++) { … for (State S: L) { if (!S.isValid() || visited.contains(S)]) continue; info.put(S, new StateInfo(info.get(queue[i]).dist + 1, queue[i])); … }
30
BFS State Search Exercise EAPC 2015 – Pacman Requirements State representation? State transitions? Valid states? Initial state? End state(s)?
31
Shortest Paths Single source shortest paths Also for the shortest path between two points A common problem during contests Do the edges have different lengths/weights? Can the edges have negative weight? No (for example for grids) Yes No BFS Dijkstra Bellman-Ford
32
Graph algorithms Dijkstra Always expand node with minimum distance (must be final) Use built-in priority queues Running time is O((E + V) log V) No decrease-key ➨ Do not remove from priority queue! 3 4 1 2 2 3 1 2 1 0 3 4 1 3 3
33
Dijkstra implementation class Node { ArrayList adj; int dist; // initially Integer.MAX_VALUE (must initialize!) Node parent; } class NodeDist implements Comparable { int i, d; // node index and distance NodeDist(int index, int dist) {…}; public int compareTo(NodeDist other) { return (d – other.d); } }
34
Dijkstra implementation (continued) void dijkstra(int source) { // can also be done for multiple sources… PriorityQueue Q = new PriorityQueue (); V[source].dist = 0; V[source].parent = null; Q.add(new NodeDist(source, 0)); while (!Q.isEmpty()) { NodeDist nd = Q.poll(); int k = nd.i; int d = nd.d; if (V[k].dist < d) continue; for (Edge e: V[k].adj) { int newDist = d + e.weight; // e.weight cannot be MAX_VALUE! if (newDist < V[e.target].dist) { V[e.target].dist = newDist; V[e.target].parent = V[k]; Q.add(new NodeDist(e.target, newDist)); } // Nodes contain distance and parent info }
35
Shortest Paths Single source shortest paths Also for the shortest path between two points A common problem during contests Do the edges have different lengths/weights? Can the edges have negative weight? No (for example for grids) Yes No BFS Dijkstra Bellman-Ford
36
Not very common, but you should know it anyway Also works with edges with negative weights/lengths Very easy to implement Can also be used to find negative cycles Running time is O(V * E) For i = 1 to V - 1 For each edge e = (u, v) if u.dist + e.weight < v.dist v.dist = u.dist + e.weight; v.pred = u;
37
Bellman-Ford implementation class Node { ArrayList adj; int dist; // initially Integer.MAX_VALUE (must initialize!) Node parent; // initially null } int N; // number of vertices void bellmanFord(int source) { V[source].dist = 0; for (int k = 0; k < N – 1; k++) { for (int i = 0; i < N; i++) { for (Edge e: V[i].adj) { int newDist = V[i].dist + e.weight; if (newDist < V[e.target].dist) { V[e.target].dist = newDist; V[e.target].parent = V[i]; } // to find negative cycle, run another iteration // if any distance changes, a negative cycle has been found }
38
All-pairs shortest path If you need the shortest path between every pair of vertices Also possible with Dijkstra, but not as easy Running time is O(V 3 ) Use distance matrix Floyd-Warshall path[i][j] = w ij ; For k = 1 to n For i = 1 to n For j = 1 to n path[i][j] = min(path[i][j], path[i][k] + path[k][j]);
39
Floyd Warshall implementation int[][] d; int N; void floydWarshall() { for (int k = 0; k < N; k++) for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]); } void main() { for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) d[i][j] = LARGE; // large enough, but LARGE + max. weight cannot overflow for (int i = 0; i < N; i++) d[i][i] = 0; for (int i = 0; i < M; i++) { … a--; b--; // if input is 1-based instead of 0-based d[a][b] = w; d[b][a] = w; } floydWarshall(); }
40
Minimum Spanning Tree Minimum spanning tree Pretty simple algorithms Use Prim or Kruskal (Running time O(E log V)) Prim E’ = {}; V’ = {v}; (arbitrary vertex) While V’ ≠ V Choose minimum-weight edge (u, v) such that u in V’ and v isn’t V’ = V’ U {v}; E’ = E’ U {(u, v)} Kruskal E’ = {}; Sort E on weight; For every (u, v) in E If E’ U {(u, v)} does not contain a cycle E’ = E’ U {(u, v)} 1 1 2 5 6 3 4 7 5 8 1 1 2 5 6 3 4 7 5 8
41
MST implementation Kruskal Requires Union-Find… Need to sort edges Prim class Node { ArrayList adj; boolean inTree; } class NodeWeight implements Comparable { int i, w, p; // node index, weight, and parent index NodeWeight(int index, int weight, int parent) {…}; public int compareTo(NodeWeight other) { return (w – other.w); } }
42
Prim implementation int N; ArrayList prim() { // returns set of edges as NodeWeights ArrayList L = new ArrayList (); PriorityQueue Q = new PriorityQueue (); for (int i = 0; i < N; i++) { // to cover disconnected graphs Q.add(new NodeWeight(i, Integer.MAX_VALUE, -1)); } while (!Q.isEmpty()) { NodeWeight nw = Q.poll(); int k = nw.i; if (V[k].inTree) continue; V[k].inTree = true; L.add(nw); for (Edge e: V[k].adj) { if (V[e.target].inTree) continue; Q.add(new NodeWeight(e.target, e.weight, k)); } // if necessary, remove special edges from L (with parent -1) return L; }
43
Euler Tours Euler tour Given a graph, find a tour that visits each edge exactly once Only possible if all vertices have even degree In case of an Euler path, the endpoints have odd degree Greedy approach works (just choose any edge) Use linked list for implementation Running time is O(V+E) Also works for directed graphs A tour that visits every vertex is very different! (NP-hard)
44
Euler Tour implementation What information do we need? What is the easiest way to compute it? class Edge { int target; int source; // not really needed here, so ignore… boolean inTour; // initially false Edge back; // pointer to reverse edge } … for (int i = 0; i < M; i++) { // reading edges int a = sc.nextInt(); int b = sc.nextInt(); a--; b--; // if input is 1-based instead of 0-based Edge e1 = new Edge(b), e2 = new Edge(a); // make the edges e1.back = e2; e2.back = e1; // set back edges V[a].adj.add(e1); V[b].adj.add(e2); } …
45
Euler Tour implementation void eulerDFS(int i, ListIterator cur) { for (Edge e: V[i].adj) { if (e.inTour) continue; // skip edges already in Euler tour e.inTour = true; e.back.inTour = true; // back edge is also in! cur.add(e.target); eulerDFS(e.target, cur); } cur.previous(); } LinkedList euler(int start) { // if path, start must have odd degree LinkedList L = new LinkedList (); L.add(start); eulerDFS(start, L.listIterator(1)); return L; }
46
Exercise Problems DFS EAPC08C – Labyrinth, EAPC12H – Hex Strongly connected components EAPC14G – Spy Network (+math) BFS BAPC12F – Fire, EAPC06A – Cheesy Chess EAPC11H – Stealth Ninja, EAPC13K – Keys Dijkstra B11B – Quick out of the harbour, EAPC06I – Sightseeing (hard) EAPC10H – Farmer John (+geometry), EAPC14B – Failing Comp. All-pairs shortest path EAPC13F – Fare dodging (Dijkstra also possible) MST EAPC08E – Power cables to sewer pipes (+DP)
Similar presentations
© 2024 SlidePlayer.com Inc.
All rights reserved.