Welcome to Subscribe On Youtube
886. Possible Bipartition
Description
We want to split a group of n people (labeled from 1 to n) into two groups of any size. Each person may dislike some other people, and they should not go into the same group.
Given the integer n and the array dislikes where dislikes[i] = [ai, bi] indicates that the person labeled ai does not like the person labeled bi, return true if it is possible to split everyone into two groups in this way.
Example 1:
Input: n = 4, dislikes = [[1,2],[1,3],[2,4]] Output: true Explanation: The first group has [1,4], and the second group has [2,3].
Example 2:
Input: n = 3, dislikes = [[1,2],[1,3],[2,3]] Output: false Explanation: We need at least 3 groups to divide them. We cannot put them in two groups.
Constraints:
1 <= n <= 20000 <= dislikes.length <= 104dislikes[i].length == 21 <= ai < bi <= n- All the pairs of
dislikesare unique.
Solutions
Solution 1
Union find.
Solution 2
Union-find is a tree-shaped data structure. As the name suggests, it is used to deal with some disjoint merge and query problems. It supports two operations:
- Find: Determine which subset an element is in, the time complexity of a single operation is $O(\alpha(n))$
- Union: Merge two subsets into one set, the time complexity of a single operation is $O(\alpha(n))$
Among them, $\alpha$ is the inverse function of Ackerman function, and its growth is extremely slow, which means that the average running time of a single operation can be considered as a small constant.
The following are commonly used templates for union search, which require proficiency. Among them:
nrepresents the number of nodespstores the parent node of each point. Initially, the parent node of each point is itself.sizeis only meaningful when the node is an ancestor node, indicating the number of points in the set where the ancestor node is located- The
find(x)function is used to find the ancestor node of the set where $x$ is located - The
union(a, b)function is used to merge the collections where $a$ and $b$ are located
p = list(range(n))
size = [1] * n
def find(x):
if p[x] != x:
# 路径压缩
p[x] = find(p[x])
return p[x]
def union(a, b):
pa, pb = find(a), find(b)
if pa == pb:
return
p[pa] = pb
size[pb] += size[pa]
For this question, we traverse each person. He and the person he dislikes should not be in the same set. If they are in the same set, a conflict will occur and false will be returned directly. If there are no conflicts, then merge all his disliked ones into the same set.
The traversal ends, indicating that there is no conflict, and true is returned.
Time complexity $O(n + m\times \alpha(n))$.
-
class Solution { private int[] p; public boolean possibleBipartition(int n, int[][] dislikes) { p = new int[n]; List<Integer>[] g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (int i = 0; i < n; ++i) { p[i] = i; } for (var e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].add(b); g[b].add(a); } for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) { return false; } p[find(j)] = find(g[i].get(0)); } } return true; } private int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } } // Solution 2 class Solution { private int[] p; public boolean possibleBipartition(int n, int[][] dislikes) { p = new int[n]; List<Integer>[] g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (int i = 0; i < n; ++i) { p[i] = i; } for (var e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].add(b); g[b].add(a); } for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) { return false; } p[find(j)] = find(g[i].get(0)); } } return true; } private int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } } -
class Solution { public: bool possibleBipartition(int n, vector<vector<int>>& dislikes) { vector<int> p(n); iota(p.begin(), p.end(), 0); unordered_map<int, vector<int>> g; for (auto& e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].push_back(b); g[b].push_back(a); } function<int(int)> find = [&](int x) -> int { if (p[x] != x) p[x] = find(p[x]); return p[x]; }; for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) return false; p[find(j)] = find(g[i][0]); } } return true; } }; // Solution 2 class Solution { public: bool possibleBipartition(int n, vector<vector<int>>& dislikes) { vector<int> p(n); iota(p.begin(), p.end(), 0); unordered_map<int, vector<int>> g; for (auto& e : dislikes) { int a = e[0] - 1, b = e[1] - 1; g[a].push_back(b); g[b].push_back(a); } function<int(int)> find = [&](int x) -> int { if (p[x] != x) p[x] = find(p[x]); return p[x]; }; for (int i = 0; i < n; ++i) { for (int j : g[i]) { if (find(i) == find(j)) return false; p[find(j)] = find(g[i][0]); } } return true; } }; -
class Solution: def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool: def find(x): if p[x] != x: p[x] = find(p[x]) return p[x] g = defaultdict(list) for a, b in dislikes: a, b = a - 1, b - 1 g[a].append(b) g[b].append(a) p = list(range(n)) for i in range(n): for j in g[i]: if find(i) == find(j): return False p[find(j)] = find(g[i][0]) return True # Solution 2 class Solution: def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool: def find(x): if p[x] != x: p[x] = find(p[x]) return p[x] g = defaultdict(list) for a, b in dislikes: a, b = a - 1, b - 1 g[a].append(b) g[b].append(a) p = list(range(n)) for i in range(n): for j in g[i]: if find(i) == find(j): return False p[find(j)] = find(g[i][0]) return True -
func possibleBipartition(n int, dislikes [][]int) bool { p := make([]int, n) g := make([][]int, n) for i := range p { p[i] = i } for _, e := range dislikes { a, b := e[0]-1, e[1]-1 g[a] = append(g[a], b) g[b] = append(g[b], a) } var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) } return p[x] } for i := 0; i < n; i++ { for _, j := range g[i] { if find(i) == find(j) { return false } p[find(j)] = find(g[i][0]) } } return true } // Solution 2 func possibleBipartition(n int, dislikes [][]int) bool { p := make([]int, n) g := make([][]int, n) for i := range p { p[i] = i } for _, e := range dislikes { a, b := e[0]-1, e[1]-1 g[a] = append(g[a], b) g[b] = append(g[b], a) } var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) } return p[x] } for i := 0; i < n; i++ { for _, j := range g[i] { if find(i) == find(j) { return false } p[find(j)] = find(g[i][0]) } } return true } -
function possibleBipartition(n: number, dislikes: number[][]): boolean { const color = new Array(n + 1).fill(0); const g = Array.from({ length: n + 1 }, () => []); const dfs = (i: number, v: number) => { color[i] = v; for (const j of g[i]) { if (color[j] === color[i] || (color[j] === 0 && dfs(j, 3 ^ v))) { return true; } } return false; }; for (const [a, b] of dislikes) { g[a].push(b); g[b].push(a); } for (let i = 1; i <= n; i++) { if (color[i] === 0 && dfs(i, 1)) { return false; } } return true; } -
impl Solution { fn dfs(i: usize, v: usize, color: &mut Vec<usize>, g: &Vec<Vec<usize>>) -> bool { color[i] = v; for &j in (*g[i]).iter() { if color[j] == color[i] || (color[j] == 0 && Self::dfs(j, v ^ 3, color, g)) { return true; } } false } pub fn possible_bipartition(n: i32, dislikes: Vec<Vec<i32>>) -> bool { let n = n as usize; let mut color = vec![0; n + 1]; let mut g = vec![Vec::new(); n + 1]; for d in dislikes.iter() { let (i, j) = (d[0] as usize, d[1] as usize); g[i].push(j); g[j].push(i); } for i in 1..=n { if color[i] == 0 && Self::dfs(i, 1, &mut color, &g) { return false; } } true } }