<p>You are given an undirected graph. You are given an integer <code>n</code> which is the number of nodes in the graph and an array <code>edges</code>, where each <code>edges[i] = [u<sub>i</sub>, v<sub>i</sub>]</code> indicates that there is an undirected edge between <code>u<sub>i</sub></code> and <code>v<sub>i</sub></code>.</p>

<p>A <strong>connected trio</strong> is a set of <strong>three</strong> nodes where there is an edge between <b>every</b> pair of them.</p>

<p>The <strong>degree of a connected trio</strong> is the number of edges where one endpoint is in the trio, and the other is not.</p>

<p>Return <em>the <strong>minimum</strong> degree of a connected trio in the graph, or</em> <code>-1</code> <em>if the graph has no connected trios.</em></p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/01/26/trios1.png" style="width: 388px; height: 164px;" />
<pre>
<strong>Input:</strong> n = 6, edges = [[1,2],[1,3],[3,2],[4,1],[5,2],[3,6]]
<strong>Output:</strong> 3
<strong>Explanation:</strong> There is exactly one trio, which is [1,2,3]. The edges that form its degree are bolded in the figure above.
</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/01/26/trios2.png" style="width: 388px; height: 164px;" />
<pre>
<strong>Input:</strong> n = 7, edges = [[1,3],[4,1],[4,3],[2,5],[5,6],[6,7],[7,5],[2,6]]
<strong>Output:</strong> 0
<strong>Explanation:</strong> There are exactly three trios:
1) [1,4,3] with degree 0.
2) [2,5,6] with degree 2.
3) [5,6,7] with degree 2.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>2 &lt;= n &lt;= 400</code></li>
	<li><code>edges[i].length == 2</code></li>
	<li><code>1 &lt;= edges.length &lt;= n * (n-1) / 2</code></li>
	<li><code>1 &lt;= u<sub>i</sub>, v<sub>i</sub> &lt;= n</code></li>
	<li><code>u<sub>i </sub>!= v<sub>i</sub></code></li>
	<li>There are no repeated edges.</li>
</ul>

class Solution:
    def minTrioDegree(self, n: int, edges: List[List[int]]) -> int:
     
 # note1: the value of each node is unique!!
 # note2: there can be many trio, each trio has a degree, you need to find the minimum degree among all degrees.   

 # sol1: best/easist: intuitive way with 3 keys below
# worst time: n^3
# space: edge
        d = defaultdict(set)
        for i, j in edges:
                d[i].add(j)
                d[j].add(i)
        num = sorted( [(e, len(d[e])) for e in d], key = lambda x:x[1]) #!! key1
        n = len(num)
        self.ans = float(inf)        

# !!key: use i,j, k index below to avoid repetition
        for i in range(n-2): #!! key2
                if self.ans <= num[i][1]*3 - 6: break
                for j in range(i+1, n-1):
                        if num[j][0] not in d[num[i][0]]: continue 
                        if self.ans <= num[i][1] + num[j][1]*2 - 6: break  #!! key3
                        for k in range(j+1, n):
                                if (num[k][0] not in d[num[i][0]] & d[num[j][0]]) or self.ans <= num[i][1] + num[j][1] + num[k][1]-6: continue
                                self.ans = num[i][1] + num[j][1] + num[k][1]-6
        return self.ans if self.ans != float(inf) else -1
        
        
 # sol2: best: use 4 keys below to avoid repetition.
 # I am not sure how it works for " if self.ans <= num[a]*3 - 6: continue " since we do not sort. Sol1 is better. 
        d = defaultdict(set)
        for i, j in edges:
                d[i].add(j)
                d[j].add(i)
        num = {i:len(d[i]) for i in d}
        self.ans = float(inf)

        for a in d:
                if self.ans <= num[a]*3 - 6: continue #!!key1
                for b in d[a]:
                        if self.ans <= num[a] + num[b]*2 - 6: continue #!!key2
                        for c in d[a] & d[b]:
                                self.ans = min(self.ans, num[a] + num[b] + num[c] - 6)
#!!key3: avoid b=>c=>a
                                d[c].discard(a) 
                        d[b].discard(a) #key4: avoid b => a
        return self.ans if self.ans != float(inf) else -1
                                
        
        
        
# sol3: dfs with rank. Time exceeded
        d = defaultdict(set)
        for i, j in edges:
                d[i].add(j)
                d[j].add(i)
        self.ans = float(inf)
#rank: r[i] = (rank of node i along a certain route, accumulated edge number)
        r = {None:(0,0)}
        past = set()
        
        def func(prev, cur, r1): #dfs
                past.add(cur)
                if cur in r:
                        if r[cur][0] == r1-3: 
                                self.ans = min(self.ans, r[prev][1] - r[cur][1] + len(d[cur]) - 6)
                        return
                r[cur] = (r1, r[prev][1] + len(d[cur]))
                for nxt in d[cur]:
                        if nxt == prev: continue
                        func(cur, nxt, r1+1)
                r.pop(cur) #backtrack
                        
        for i in range(1, n+1):
                if i not in past: func(None, i, 0)
        return self.ans if self.ans != float(inf) else -1
             
                                
                
                
44000