Skip to Content

Flight Routes Check

Explicación

El teorema que se usa aquí es que si un vértice puede tanto alcanzar como ser alcanzado por todos los demás, entonces todo vértice de este grafo puede alcanzar a todos los demás.

Digamos que can[u][v]\texttt{can[u][v]} es verdadero si se puede ir del vértice uu al vértice vv a través de una serie de aristas. Además, definamos el grafo dirigido dado en el enunciado como GG y su reverso (donde una arista uvu \rightarrow v se convierte en vuv \rightarrow u) como GG'. Entonces, si can[1][x]\texttt{can[1][x]} para 1xn1 \leq x \leq n tanto en GG como en GG', la respuesta es “YES”.

Para computar can[1][x]\texttt{can[1][x]}, podemos ejecutar un DFS desde el vértice 11 y comprobar si se puede alcanzar el vértice xx para todo 1xn1 \leq x \leq n. Si no podemos, entonces imprimimos 11 xx si estamos ejecutando el DFS en GG y xx 11 en caso contrario.

Demostración

Hagamos una demostración por contradicción. Supongamos que can[1][x]\texttt{can[1][x]} es verdadero para todos los vértices xx tanto en GG como en GG', y existe un par de vértices u,vu, v tal que can[u][v]=false\texttt{can[u][v]} = \texttt{false}. Como can[1][u]\texttt{can[1][u]} es verdadero en GG', entonces can[u][1]\texttt{can[u][1]} debe ser verdadero en GG. Además, can[1][v]\texttt{can[1][v]} debe ser verdadero en GG. Así, se puede viajar de u1vu \rightarrow 1 \rightarrow v, lo que contradice que can[u][v]=false\texttt{can[u][v]} = \texttt{false}. Por lo tanto, can[u][v]\texttt{can[u][v]} es verdadero para todos los vértices u,vu, v.

Implementación

Complejidad temporal: O(N)\mathcal{O}(N)

#include <bits/stdc++.h> using namespace std; void dfs(int pos, vector<vector<int>> &flights, vector<bool> &visited) { for (int c : flights[pos]) { if (visited[c]) continue; visited[c] = true; dfs(c, flights, visited); } } int main() { int n, m; cin >> n >> m; vector<vector<int>> forward_graph(n); vector<vector<int>> reverse_graph(n); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; a--; b--; forward_graph[a].push_back(b); reverse_graph[b].push_back(a); } // Forward Pass vector<bool> visited(n); dfs(0, forward_graph, visited); for (int i = 1; i < n; i++) { if (visited[i] == false) { cout << "NO\n"; cout << 1 << " " << i + 1 << "\n"; return 0; } } // Reverse Pass visited = vector<bool>(n, false); dfs(0, reverse_graph, visited); for (int i = 1; i < n; i++) { if (visited[i] == false) { cout << "NO" << endl; cout << i + 1 << " " << 1 << "\n"; return 0; } } cout << "YES" << endl; }
import java.io.*; import java.util.*; public class FlightRoutesCheck { static int n, m; static boolean[] vis; public static void main(String[] args) { Kattio io = new Kattio(); n = io.nextInt(); m = io.nextInt(); // Stores the graph we are given (or G) ArrayList<ArrayList<Integer>> adj1 = new ArrayList<>(); // Stores the reverse of the graph we are given (or G') ArrayList<ArrayList<Integer>> adj2 = new ArrayList<>(); for (int i = 0; i < n; i++) { adj1.add(new ArrayList<>()); adj2.add(new ArrayList<>()); } for (int i = 0; i < m; i++) { int a = io.nextInt() - 1; int b = io.nextInt() - 1; adj1.get(a).add(b); adj2.get(b).add(a); } // Run dfs to check if you can reach all other vertices from vertex 1 vis = new boolean[n]; dfs(0, adj1); for (int i = 0; i < n; i++) { // If some vertex wasn't visited, that means that we cannot reach // all other vertices, so we return NO if (!vis[i]) { io.println("NO"); io.println(1 + " " + (i + 1)); io.close(); return; } } // Run dfs to check if you can reach vertex 1 from all other vertices vis = new boolean[n]; dfs(0, adj2); for (int i = 0; i < n; i++) { // If some vertex wasn't visited, that means vertex 1 cannot be // reached from all other vertices, so we return NO if (!vis[i]) { io.println("NO"); io.println((i + 1) + " " + 1); io.close(); return; } } // If we haven't exited yet, that means the answer is YES io.println("YES"); io.close(); } static void dfs(int v, ArrayList<ArrayList<Integer>> adj) { vis[v] = true; for (int to : adj.get(v)) { if (!vis[to]) { dfs(to, adj); } } } // CodeSnip{Kattio} }
from typing import List, Set n, m = map(int, input().split()) forward_graph = [[] for _ in range(n)] reverse_graph = [[] for _ in range(n)] for _ in range(m): a, b = map(int, input().split()) forward_graph[a - 1].append(b - 1) reverse_graph[b - 1].append(a - 1) def dfs(graph: List[List[int]]) -> Set[int]: """ Performs a depth-first traversal of a graph starting from node 0. Removes each node reached from a set of all nodes. Once complete, the set will only contain unvisited nodes and is then returned. :param graph: An adjacency list representing a graph. :return: A set containing all unvisited nodes in the depth-first traversal. """ unvisited = set(range(1, n)) stack = [0] while stack: curr = stack.pop() for adj in graph[curr]: if adj in unvisited: stack.append(adj) unvisited.remove(adj) return unvisited # These will be empty if there are no unvisited nodes unvisited_fwd = dfs(forward_graph) unvisited_rev = dfs(reverse_graph) if unvisited_fwd: print("NO") print(f"1 {unvisited_fwd.pop() + 1}") elif unvisited_rev: print("NO") print(f"{unvisited_rev.pop() + 1} 1") else: print("YES")

El problema también se puede resolver usando componentes fuertemente conexas (SCC).