/
Honors Track: Competitive Programming Honors Track: Competitive Programming

Honors Track: Competitive Programming - PowerPoint Presentation

min-jolicoeur
min-jolicoeur . @min-jolicoeur
Follow
384 views
Uploaded On 2020-01-25

Honors Track: Competitive Programming - PPT Presentation

Honors Track Competitive Programming amp Problem Solving Fun with Graphs I Kevin Verbeek Graph algorithms Standard Algorithms DFS BFS Single source shortest path Allpairs shortest path Minimum spanning tree ID: 773786

Share:

Link:

Embed:

Download Presentation from below link

Download Presentation The PPT/PDF document "Honors Track: Competitive Programming" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.


Presentation Transcript

Honors Track:Competitive Programming& Problem SolvingFun with Graphs I Kevin Verbeek

Graph algorithmsStandard AlgorithmsDFSBFSSingle source shortest pathAll-pairs shortest pathMinimum spanning tree Euler tour Bipartite matching Max-flow Fun with Graphs I Fun with Graphs II

Graph representationGraph representationsAdjacency matrix ➨ easy, but slow and memory-heavy Adjacency list ➨ straightforward and efficient C++ Java struct Edge { int target; int weight; … }; struct Node { vector < Edge > adj ; bool visited; … }; Node V[10005]; class Edge { int target; int weight; … } class Node { ArrayList < Edge > adj ; boolean visited; … } Node [] V;

Graph InputReading Inputvoid 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 }

Depth First SearchDFSEasy to implement with recursive functionRunning time is O(V + E) v oid 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

Flood FillFlood FillFind connected area in a gridStart from given set of seedsRun DFS for each seedCan use “visited” to keep track Graphs Also works for graphs in general “Find nodes connected to seeds” class Node { ArrayList < Edge > adj ; boolean visited; boolean obs;}

Flood Fillint[] 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<Pair> seeds = … // read input… for (Pair p: seeds) { dfs(p.x, p.y); } // do something with visited nodes}

Flood FillFor 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<Integer> seeds = … // read input… for (Integer i: seeds) { dfs(i); } // do something with visited nodes}

Connected ComponentsConnected ComponentsFind number of disconnected partsNodes of same part get same numberSimilar to Flood FillCommon part of problems (2 in EAPC!) Dynamic version Edges are added dynamically Use Union-find class Node { ArrayList <Edge > adj ; boolean visited; int comp; // can use -1 to replace visited}

Connected ComponentsNode[] 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}

A B C D E F Strongly Connected Components 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 E F

Tarjan’s algorithmTarjan’s algorithmPerform a DFSAssign number to each node based on DFS orderFor 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 E F

Tarjan’s algorithm Stack 0 4 1 3 6 7 5 11 2 0 1 2 3 4 5 6 7 8 9 10 11 13 8 9 10 12 12 12 13

Tarjan implementationTarjan implementationKeep track of current indexMust know if node is on stack in O(1) time (!)Compute component index for each node class Node { ArrayList < Edge > 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 <Integer> stack = new ArrayDeque<Integer>();

Tarjan implementationvoid 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++; } }

Strongly Connected ComponentsProblemHow many edges are needed to make graph strongly connected?SolutionCompute strongly connected componentsCompute 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 graphMake 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 E F

Biconnected ComponentsBiconnected ComponentsDefined for undirected graphsSubgraph 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 nodesReduced graph is a tree Algorithm similar to Tarjan’s2-edge-connected Subgraph is 2-edge-connected if two edges must be removed to disconnect the subgraph2-edge-connected component is subset of nodes

Shortest PathsSingle source shortest pathsAlso for the shortest path between two pointsA common problem during contests Do the edges have different lengths/weights? Can the edges have negative weight? No (for example for grids) Yes Yes No BFS Dijkstra Bellman-Ford

Breadth First SearchBFSSlightly harder to implement than DFSUseful for simple shortest path problemsEasy to implement with a queueRunning time is O(V + E ) Grid-BFS These problems often occurAlmost always solvable using a BFSYou sometimes need to be creative with states Hint: For Grid-BFS, add a wall around the grid ## #### # # # # # # # ### # # p# # #######

Graph BFSclass Node { ArrayList < Integer > adj ; int dist; // dist = -1 if not visited, initially -1 (must be set!) } void main() { // read input… including source(s) ArrayList<Integer> queue = new ArrayList < Integer >(); // 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); } }}

State SearchBFS for StatesBFS works well for state space searching“How many steps to get from one state to another?”Number of states depends on state spaceExample: 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?

State SearchRequirementsState representation(CanLeft, MisLeft, BoatPos ), 0 ≤ CanLeft /MisLeft ≤ 3, BoatPos = 0/1 State transitionsPut 1 or 2 people on boatValid states#cannibals cannot outnumber #missionaries on either side Initial stateCanLeft = MisLeft = 3 and BoatPos = 0End state(s)CanLeft = MisLeft = 0

StatesGeneral Setup class State { int canLeft , misLeft , boatPos ; State( int c, int m, int b) {…}; boolean isValid () {…}; boolean isEnd() {…}; ArrayList<State> trans() {…};}void main() { ArrayList<State> queue = new ArrayList<State>(); // 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< State> L = queue[ i ].trans(); for ( State S: L) { if (! S.isValid () || visited[S] ) continue ; queue.add(S); } }}

State Checksclass 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<State> trans() {…};}class State { int canLeft, misLeft, boatPos; State(int c, int m, int b) {…}; boolean isValid () {…}; boolean isEnd () { return ( misLeft + canLeft == 0); } ArrayList<State> trans() {…};}

State Transitionsclass State { int canLeft , misLeft , boatPos; State( int c, int m, int b) {…}; boolean isValid() {…}; boolean isEnd () {…}; ArrayList<State> trans() { ArrayList<State> L = new ArrayList<State>(); 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; }}

State VisitedHow to check if State has already been visited?Method 1Keep 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 2Keep HashSet of visited States  Simple and can hold arbitrary States  Cannot store extra info in State class (or override hashcode())  Slight computational overhead

State Visited Method 1class 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<State> 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); } }

State Visited Method 2class State { int canLeft , misLeft , boatPos; … } … HashSet < State > visited = new HashSet <State>();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<State> L = queue[i].trans(); for (State S: L) { if (!S.isValid() || visited.contains(S)]) continue; visited.add(S); queue.add (S); } }

State Visited Method 2 (continued)class StateInfo { int dist; State parent; … } … HashMap < State,StateInfo > info = new HashMap < State,StateInfo >();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])); … } }

BFS State SearchExerciseEAPC 2015 – PacmanRequirementsState representation? State transitions? Valid states? Initial state? End state(s)?

Shortest PathsSingle source shortest pathsAlso for the shortest path between two pointsA common problem during contests Do the edges have different lengths/weights? Can the edges have negative weight? No (for example for grids) Yes Yes No BFS Dijkstra Bellman-Ford

Graph algorithmsDijkstraAlways 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

Dijkstra implementationclass Node { ArrayList < Edge > adj ; int dist; // initially Integer.MAX_VALUE (must initialize!) Node parent; } class NodeDist implements Comparable < NodeDist> { int i, d; // node index and distance NodeDist(int index, int dist) {…}; public int compareTo(NodeDist other) { return (d – other.d); }}

Dijkstra implementation (continued)void dijkstra ( int source) { // can also be done for multiple sources… PriorityQueue <NodeDist > Q = new PriorityQueue < NodeDist >(); 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}

Shortest PathsSingle source shortest pathsAlso for the shortest path between two pointsA common problem during contests Do the edges have different lengths/weights? Can the edges have negative weight? No (for example for grids) Yes Yes No BFS Dijkstra Bellman-Ford

Bellman-FordBellman-FordNot very common, but you should know it anywayAlso works with edges with negative weights/lengthsVery 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;

Bellman-Ford implementationclass Node { ArrayList < Edge > 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 }

All-pairs shortest pathAll-pairs shortest pathIf you need the shortest path between every pair of verticesAlso possible with Dijkstra, but not as easyRunning time is O(V 3 ) Use distance matrix Floyd- Warshall path[i ][j] = wij; 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]);

Floyd Warshall implementationint[][] 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();}

Minimum Spanning TreeMinimum spanning treePretty simple algorithmsUse 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

MST implementationKruskalRequires Union-Find… Need to sort edges Prim class Node { ArrayList < Edge > adj ; boolean inTree ; } class NodeWeight implements Comparable < NodeWeight> { 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); }}

Prim implementationint N; ArrayList < NodeWeight > prim() { // returns set of edges as NodeWeights ArrayList < NodeWeight > L = new ArrayList <NodeWeight>(); PriorityQueue< NodeWeight > Q = new PriorityQueue<NodeWeight>(); 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;}

Euler ToursEuler tourGiven a graph, find a tour that visits each edge exactly onceOnly possible if all vertices have even degreeIn case of an Euler path, the endpoints have odd degreeGreedy approach works (just choose any edge) Use linked list for implementation Running time is O( V +E) Also works for directed graphsA tour that visits every vertex is very different! (NP-hard)

Euler Tour implementationWhat 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);}…

Euler Tour implementationvoid eulerDFS ( int i , ListIterator < Integer> 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<Integer> euler(int start) { // if path, start must have odd degree LinkedList<Integer> L = new LinkedList<Integer>(); L.add(start); eulerDFS(start, L.listIterator(1)); return L;}

Exercise ProblemsDFSEAPC08C – Labyrinth, EAPC12H – HexStrongly connected componentsEAPC14G – Spy Network (+math)BFSBAPC12F – 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)