Paths in Maze That Lead to Same Room
Count triangles in an undirected graph — for each edge (u, v), count neighbors shared by both, then divide.
O(V * E / 32) Space O(V * V / 8) Problem#
An curriculum-track problem. Given an undirected graph of n rooms and a list of corridors, count the number of length-3 cycles (triangles) — sets of three rooms pairwise connected.
Examples#
n = 5, corridors = [[1,2],[5,2],[4,1],[2,4],[3,1],[3,4]]→2n = 4, corridors = [[1,2],[3,4]]→0
Constraints#
2 <= n <= 1000,1 <= corridors.length <= 5 * 10^4.
Approach#
For each edge (u, v), the number of triangles containing it equals the number of common neighbors. Use bitset adjacency to compute the intersection size in O(n / 64). Sum across all edges, divide by 3 (each triangle is counted by each of its three edges).
Solution#
class Solution {public: int numberOfPaths(int n, vector<vector<int>>& corridors) { vector<bitset<1001>> adj(n + 1); for (auto& c : corridors) { adj[c[0]].set(c[1]); adj[c[1]].set(c[0]); } long long total = 0; for (auto& c : corridors) { total += (adj[c[0]] & adj[c[1]]).count(); } return (int)(total / 3); }};func numberOfPaths(n int, corridors [][]int) int { // Bitset adjacency: each row is ceil((n+1)/64) uint64 words. words := (n+1+63)/64 + 1 adj := make([][]uint64, n+1) for i := range adj { adj[i] = make([]uint64, words) } setBit := func(row []uint64, b int) { row[b/64] |= uint64(1) << uint(b%64) } for _, c := range corridors { setBit(adj[c[0]], c[1]) setBit(adj[c[1]], c[0]) } var total int64 for _, c := range corridors { a, b := adj[c[0]], adj[c[1]] var cnt int for i := 0; i < words; i++ { cnt += bits.OnesCount64(a[i] & b[i]) } total += int64(cnt) } return int(total / 3)}from typing import List
class Solution: def numberOfPaths(self, n: int, corridors: List[List[int]]) -> int: adj = [0] * (n + 1) for u, v in corridors: adj[u] |= 1 << v adj[v] |= 1 << u total = 0 for u, v in corridors: total += bin(adj[u] & adj[v]).count("1") return total // 3function numberOfPaths(n, corridors) { // BigInt-as-bitset: bit i of adj[u] set => edge (u, i) exists. const adj = new Array(n + 1).fill(0n); for (const [u, v] of corridors) { adj[u] |= 1n << BigInt(v); adj[v] |= 1n << BigInt(u); } const popcount = (x) => { let c = 0; while (x > 0n) { x &= x - 1n; c++; } return c; }; let total = 0; for (const [u, v] of corridors) { total += popcount(adj[u] & adj[v]); } return Math.floor(total / 3);}class Solution { public int numberOfPaths(int n, int[][] corridors) { // BitSet adjacency: bit i of adj[u] set => edge (u, i) exists. java.util.BitSet[] adj = new java.util.BitSet[n + 1]; for (int i = 0; i <= n; i++) adj[i] = new java.util.BitSet(n + 1); for (int[] c : corridors) { adj[c[0]].set(c[1]); adj[c[1]].set(c[0]); } long total = 0; for (int[] c : corridors) { java.util.BitSet inter = (java.util.BitSet) adj[c[0]].clone(); inter.and(adj[c[1]]); total += inter.cardinality(); } return (int) (total / 3); }}function numberOfPaths(n: number, corridors: number[][]): number { // BigInt-as-bitset: bit i of adj[u] set => edge (u, i) exists. const adj: bigint[] = new Array(n + 1).fill(0n); for (const [u, v] of corridors) { adj[u] |= 1n << BigInt(v); adj[v] |= 1n << BigInt(u); } const popcount = (x: bigint): number => { let c = 0; while (x > 0n) { x &= x - 1n; c++; } return c; }; let total = 0; for (const [u, v] of corridors) { total += popcount(adj[u] & adj[v]); } return Math.floor(total / 3);}Editorial#
For each edge (u, v), common neighbors of u and v complete a triangle (u, v, w). The bitset AND gives the count of such w in O(n / word_size). Each triangle’s three edges each contribute the same triangle once, hence the division by 3.
Complexity#
- Time: O(V * E / 64).
- Space: O(V^2 / 8).
Concept revision#
Related#