有 N 個人(編號 1, 2, ..., N),要依照 dislikes 去分成二組,在 dislikes 中,每組 [a, b] 代表該編號的二人不能在同一組。
回傳 True 如果可以順利分成二組,否則 False。
這題蠻有意思的,我第一念頭想到 backtrace 可以硬解,但太慢,我放棄用 DP 與 遞迴函數。
說到兩兩成對,就想到最近的表格式DP,這題一樣可以用表格式DP,最後用 DFS 的遞迴函數可以比 backtrace 快上不少。
只要表格上可以選成每列每欄都只有一組配對,那就有可行的分組,也就是True。
抛開遞迴,因為平面化的話,一有答案可以馬上回傳,而遞迴函數會一路收斂到最上方,才能停止。要是深度深,會多花上一些時間。
被自我侷限,得用別的方法來。
如果 [a,b] 是 [1, 3],代表 1, 3 不能同組。這個分組沒有順序,也就是 組1、組2 對調是沒差的。
但如果用人的思維去硬分,肯定要選一組當基準去分配,就會產生順序的問題。
[1,3],[2,4],[3,4],答案是 True 分成 [1,4],[2,3] 。但如果都選 組1 當基準,輪到 [2,4] 分配的時候,要是組別分成 [1,2],[3,4],
那下一個遇到分[3,4],就會無法依規則分,然後回傳 False 。
要是換個順序 [1,3],[3,4],[2,4],選基準去分,就會得到一組可行的,回傳 True。
[1,3] 後面接 [3,4] ,頭尾相接,可以想是一條討厭鏈,不同討厭鏈之間任意分配是沒關係的,但在同一條就該按順序來,如果可以串起來,就該優先分配。
依題目所限,dislikes 已經有排列過,所會先拿到討厭鏈的頭,想辦法後面依序先分配。
如果開頭是 [1,3] ,下一個要以 3 為開頭,[3, 4],再以 4 開頭,再以 2 開頭,這不是一個有固定順序的,但要把 N 個開頭都找過。
討厭鏈一定要順利分完,依討厭鏈去分配是很簡單的,二人不要在同一組,分不下去就一定是回傳 False。
首先全部人都先在 組1 ,如果 [a, b] 都在 組1,把 b 移到 組2。如果 [a,b] 都在 組2 ,即無法分,回傳 False。
(用這思考看看:N = 3, dislikes = [[1,2],[1,3],[2,3]])
要依照討厭鏈順序去分的方法︰
用一個 set(1,2,...,N) ,跟 stack,遇到數字就從 set 中取到 stack。若 stack 為空,就從 set 任取一個放入stack,直到把 set 取光。
class Solution: def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool: memo = defaultdict(list) for me, you in dislikes: memo[me].append(you) memo[you].append(me) group1 = set(range(1,N+1)) group2 = set() candidate = set(range(1, N+1)) stack = [] while candidate: stack.append(candidate.pop()) while stack: i = stack.pop(0) for hate in memo[i]: if i in group1 and hate in group1: group1.discard(hate) group2.add(hate) elif i in group2 and hate in group2: return False if hate in candidate: stack.append(hate) candidate.discard(hate) return TrueC#
public class Solution { public bool PossibleBipartition(int N, int[][] dislikes) { var relation = new Dictionary>(); var group1 = new HashSet (); var group2 = new HashSet (); var candidate = new HashSet (); for(int i = 1; i <= N; i++) { relation[i] = new HashSet (); group1.Add(i); candidate.Add(i); } for(int i = 0; i < dislikes.Length; i++) { relation[dislikes[i][0]].Add(dislikes[i][1]); relation[dislikes[i][1]].Add(dislikes[i][0]); } var queue = new Queue (); while(candidate.Count > 0) { queue.Enqueue(candidate.First()); candidate.Remove(candidate.First()); while(queue.Count > 0) { int head = queue.Dequeue(); foreach(int node in relation[head]) { if(group1.Contains(head) && group1.Contains(node)) { group1.Remove(node); group2.Add(node); } else if(group2.Contains(head) && group2.Contains(node)) { return false; } if(candidate.Contains(node)) { candidate.Remove(node); queue.Enqueue(node); } } } } return true; } }