Loud and Rich
DFS with memo — for each person, the answer is the minimum-quietness among self and all richer-than-self (transitively).
4 min read
graph dfs memo topological-sort
Problem#
richer[i] = [a, b] means person a is richer than person b. For each x, return the index of the quietest person among those at least as rich as x (including x).
Examples#
richer=[[1,0],[2,1],[3,1],[3,7],[4,3],[5,3],[6,3],[7,0]], quiet=[3,2,5,4,6,1,7,0]→[5,5,2,5,4,5,6,7].
Constraints#
1 <= n <= 500.
Approach#
Build a DAG: edge a → b iff a is richer than b. For each x, DFS over all reachable from x (richer-than-x) and pick the one with the smallest quiet. Memoize.
Solution#
class Solution { vector<vector<int>> adj; vector<int> quiet; vector<int> ans; int dfs(int x) { if (ans[x] != -1) return ans[x]; ans[x] = x; for (int y : adj[x]) { int c = dfs(y); if (quiet[c] < quiet[ans[x]]) ans[x] = c; } return ans[x]; }public: vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& q) { int n = q.size(); quiet = q; adj.assign(n, {}); ans.assign(n, -1); for (auto& r : richer) adj[r[1]].push_back(r[0]); for (int i = 0; i < n; ++i) dfs(i); return ans; }};func loudAndRich(richer [][]int, quiet []int) []int { n := len(quiet) adj := make([][]int, n) for _, r := range richer { adj[r[1]] = append(adj[r[1]], r[0]) } ans := make([]int, n) for i := range ans { ans[i] = -1 } var dfs func(x int) int dfs = func(x int) int { if ans[x] != -1 { return ans[x] } ans[x] = x for _, y := range adj[x] { c := dfs(y) if quiet[c] < quiet[ans[x]] { ans[x] = c } } return ans[x] } for i := 0; i < n; i++ { dfs(i) } return ans}from typing import List
class Solution: def loudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]: n = len(quiet) adj: List[List[int]] = [[] for _ in range(n)] for a, b in richer: adj[b].append(a) ans = [-1] * n
def dfs(x: int) -> int: if ans[x] != -1: return ans[x] ans[x] = x for y in adj[x]: c = dfs(y) if quiet[c] < quiet[ans[x]]: ans[x] = c return ans[x]
for i in range(n): dfs(i) return ansfunction loudAndRich(richer, quiet) { const n = quiet.length; const adj = Array.from({ length: n }, () => []); for (const [a, b] of richer) adj[b].push(a); const ans = new Array(n).fill(-1); const dfs = (x) => { if (ans[x] !== -1) return ans[x]; ans[x] = x; for (const y of adj[x]) { const c = dfs(y); if (quiet[c] < quiet[ans[x]]) ans[x] = c; } return ans[x]; }; for (let i = 0; i < n; i++) dfs(i); return ans;}class Solution { private List<List<Integer>> adj; private int[] quiet; private int[] ans;
public int[] loudAndRich(int[][] richer, int[] quiet) { int n = quiet.length; this.quiet = quiet; adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); for (int[] r : richer) adj.get(r[1]).add(r[0]); ans = new int[n]; Arrays.fill(ans, -1); for (int i = 0; i < n; i++) dfs(i); return ans; }
private int dfs(int x) { if (ans[x] != -1) return ans[x]; ans[x] = x; for (int y : adj.get(x)) { int c = dfs(y); if (quiet[c] < quiet[ans[x]]) ans[x] = c; } return ans[x]; }}function loudAndRich(richer: number[][], quiet: number[]): number[] { const n = quiet.length; const adj: number[][] = Array.from({ length: n }, () => []); for (const [a, b] of richer) adj[b].push(a); const ans: number[] = new Array(n).fill(-1); const dfs = (x: number): number => { if (ans[x] !== -1) return ans[x]; ans[x] = x; for (const y of adj[x]) { const c = dfs(y); if (quiet[c] < quiet[ans[x]]) ans[x] = c; } return ans[x]; }; for (let i = 0; i < n; i++) dfs(i); return ans;}Editorial#
Direction matters: edges point “less rich → more rich” so DFS from x enumerates all richer people. Memoization makes the whole sweep linear in nodes plus edges; the DAG structure (richness is a partial order) guarantees no cycles.
Complexity#
- Time:
O(n + e). - Space:
O(n + e).
Concept revision#
Related#