|
| 1 | +<!--?title Strong Orientation--> |
| 2 | + |
| 3 | +# Graph Strong Orientation |
| 4 | + |
| 5 | +A **strong orientation** of an undirected graph is an assignment of a direction to each edge that makes it a [strongly connected graph](./graph/strongly-connected-components.html). |
| 6 | +That is, after the *orientation* we should be able to visit any vertex from any vertex by following the directed edges. |
| 7 | + |
| 8 | +## Solution |
| 9 | + |
| 10 | +Of course, this cannot be done to *every* graph. |
| 11 | +Consider a [bridge](./graph/bridge-searching.html) in a graph. |
| 12 | +We have to assign a direction to it and by doing so we make this bridge "crossable" in only one direction. That means we can't go from one of the bridge's ends to the other, so we can't make the graph strongly connected. |
| 13 | + |
| 14 | +Now consider a [DFS](./graph/depth-first-search.html) through a bridgeless connected graph. |
| 15 | +Clearly, we will visit each vertex. |
| 16 | +And since there are no bridges, we can remove any DFS tree edge and still be able to go |
| 17 | +from below the edge to above the edge by using a back edge. |
| 18 | +From this follows that from any vertex we can go to the DFS tree root by using back edges. |
| 19 | +Then, from the DFS tree root we can visit any vertex we choose. |
| 20 | +We found a strong orientation! |
| 21 | + |
| 22 | +In other words, to strongly orient a bridgeless connected graph, |
| 23 | +run a DFS on it and let the DFS tree edges point away from the DFS root and |
| 24 | +all other edges from the descendant to the ancestor in the DFS tree. |
| 25 | + |
| 26 | +The result that bridgeless connected graphs are exactly the graphs that have strong orientations is called **Robbins' theorem**. |
| 27 | + |
| 28 | +## Problem extension |
| 29 | + |
| 30 | +Let's consider the problem of finding a graph orientation so that the number of SCCs is minimal. |
| 31 | + |
| 32 | +Of course, each graph component can be considered separately. |
| 33 | +Now, since only bridgeless graphs are strongly orientable, let's remove all bridges temporarily. |
| 34 | +We end up with some number of bridgeless components |
| 35 | +(exactly *how many components there were at the beginning* + *how many bridges there were*) |
| 36 | + and we know that we can strongly orient each of them. |
| 37 | + |
| 38 | +We were only allowed to orient edges, not remove them, but it turns out we can orient the bridges arbitrarily. |
| 39 | +Of course, the easiest way to orient them is to run the algorithm described above without modifications on each original connected component. |
| 40 | + |
| 41 | +### Implementation |
| 42 | + |
| 43 | +Here, the input is *n* — the number of vertices, *m* — the number of edges, then *m* lines describing the edges. |
| 44 | + |
| 45 | +The output is the minimal number of SCCs on the first line and on the second line |
| 46 | +a string of *m* characters, |
| 47 | +either `>` — telling us that the corresponding edge from the input |
| 48 | +is oriented from the left to the right vertex (as in the input), |
| 49 | +or `<` — the opposite. |
| 50 | + |
| 51 | +This is a bridge search algorithm modified to also orient the edges, |
| 52 | +you can as well orient the edges as a first step and count the SCCs on the oriented graph as a second. |
| 53 | + |
| 54 | +```cpp |
| 55 | +#include <cstdio> |
| 56 | +#include <vector> |
| 57 | + |
| 58 | +using namespace std; |
| 59 | + |
| 60 | +typedef pair<int, int> pii; |
| 61 | + |
| 62 | +vector<vector<pii>> adj; // adjacency list - pairs (vertex index, edge index) |
| 63 | +vector<pii> edges; |
| 64 | + |
| 65 | +vector<int> index, low; |
| 66 | +int tmpbridges; |
| 67 | +vector<int> orient; |
| 68 | +vector<bool> edge_used; |
| 69 | +void find_bridges(int v, int pedge = -1) { |
| 70 | + static int time = 0; |
| 71 | + low[v] = index[v] = time++; |
| 72 | + for (auto p : adj[v]) { |
| 73 | + if (edge_used[p.second]) continue; |
| 74 | + int nv = p.first; |
| 75 | + if (index[nv] == -1) { // if nv is not visited yet |
| 76 | + edge_used[p.second] = true; |
| 77 | + find_bridges(nv, p.second); |
| 78 | + orient[p.second] = v != edges[p.second].first; |
| 79 | + if (low[nv] < low[v]) { |
| 80 | + low[v] = low[nv]; |
| 81 | + } |
| 82 | + if (low[nv] > index[v]) { |
| 83 | + // a bridge between v and nv |
| 84 | + tmpbridges++; |
| 85 | + } |
| 86 | + } else if (p.second != pedge) { |
| 87 | + edge_used[p.second] = true; |
| 88 | + orient[p.second] = v != edges[p.second].first; |
| 89 | + if (low[nv] < low[v]) { |
| 90 | + low[v] = low[nv]; |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +int main() { |
| 97 | + int n, m; |
| 98 | + scanf("%d %d", &n, &m); |
| 99 | + adj.resize(n); |
| 100 | + index.resize(n, -1); |
| 101 | + low.resize(n, -1); |
| 102 | + orient.resize(m); |
| 103 | + edges.resize(m); |
| 104 | + edge_used.resize(m); |
| 105 | + for (int i = 0; i < m; i++) { |
| 106 | + int a, b; |
| 107 | + scanf("%d %d", &a, &b); |
| 108 | + a--; b--; |
| 109 | + adj[a].push_back({b, i}); |
| 110 | + adj[b].push_back({a, i}); |
| 111 | + edges[i] = {a, b}; |
| 112 | + } |
| 113 | + int result = 0; |
| 114 | + for (int v = 0; v < n; v++) { |
| 115 | + if (index[v] == -1) { |
| 116 | + tmpbridges = 0; |
| 117 | + find_bridges(v); |
| 118 | + result += tmpbridges+1; |
| 119 | + } |
| 120 | + } |
| 121 | + printf("%d\n", result); |
| 122 | + for (int i = 0; i < m; i++) { |
| 123 | + if (orient[i] == 0) { |
| 124 | + printf(">"); |
| 125 | + } else { |
| 126 | + printf("<"); |
| 127 | + } |
| 128 | + } |
| 129 | + puts(""); |
| 130 | +} |
| 131 | +``` |
| 132 | +
|
| 133 | +
|
| 134 | +
|
| 135 | +## Practice Problems |
| 136 | +
|
| 137 | +* [26th Polish OI - Osiedla](https://szkopul.edu.pl/problemset/problem/nldsb4EW1YuZykBlf4lcZL1Y/site/) |
0 commit comments