|
| 1 | +# 765. Couples Holding Hands |
| 2 | + |
| 3 | +**<font color=red>难度: Hard</font>** |
| 4 | + |
| 5 | +## 刷题内容 |
| 6 | + |
| 7 | +> 原题连接 |
| 8 | +
|
| 9 | +* https://leetcode.com/problems/couples-holding-hands/ |
| 10 | + |
| 11 | +> 内容描述 |
| 12 | +
|
| 13 | +``` |
| 14 | +N couples sit in 2N seats arranged in a row and want to hold hands. We want to know the minimum number of swaps so that every couple is sitting side by side. A swap consists of choosing any two people, then they stand up and switch seats. |
| 15 | +
|
| 16 | +The people and seats are represented by an integer from 0 to 2N-1, the couples are numbered in order, the first couple being (0, 1), the second couple being (2, 3), and so on with the last couple being (2N-2, 2N-1). |
| 17 | +
|
| 18 | +The couples' initial seating is given by row[i] being the value of the person who is initially sitting in the i-th seat. |
| 19 | +
|
| 20 | +Example 1: |
| 21 | +
|
| 22 | +Input: row = [0, 2, 1, 3] |
| 23 | +Output: 1 |
| 24 | +Explanation: We only need to swap the second (row[1]) and third (row[2]) person. |
| 25 | +Example 2: |
| 26 | +
|
| 27 | +Input: row = [3, 2, 0, 1] |
| 28 | +Output: 0 |
| 29 | +Explanation: All couples are already seated side by side. |
| 30 | +Note: |
| 31 | +
|
| 32 | +len(row) is even and in the range of [4, 60]. |
| 33 | +row is guaranteed to be a permutation of 0...len(row)-1. |
| 34 | +``` |
| 35 | + |
| 36 | +## 解题方案 |
| 37 | + |
| 38 | +> 思路 1 |
| 39 | +******- 时间复杂度: O(N)******- 空间复杂度: O(N)****** |
| 40 | + |
| 41 | +证明过程参考[Java/C++ O(N) solution using cyclic swapping](https://leetcode.com/problems/couples-holding-hands/discuss/113362/JavaC%2B%2B-O(N)-solution-using-cyclic-swapping) |
| 42 | + |
| 43 | +以下所有的group指的都是connected component |
| 44 | + |
| 45 | +并查集的思想,by saying "resolving a group", we mean placing the elements at each index contained in the group to |
| 46 | +their expected positions at the end of the swapping process. |
| 47 | + |
| 48 | +先assume一个论点,size为n的group需要n-1次swap来使得所有的元素放在它应该在的index上 |
| 49 | + |
| 50 | +- Base case:如果是一个size 为1的group,我们只需要0次swap就可以让所有的元素放在它应该在的index上 |
| 51 | +- Induction case:如果是一个size 为k+1的group,那么这个group就是```... --> i --> p --> ... --> j --> q --> ....```的形式, |
| 52 | +假设i本应该在的位置是j所在的位置,j本应该在的位置是i所在的位置,那么我们将i和j互相换一下,就将这个size为 k+1的group分成了两个disjoint的group。 |
| 53 | +假设这两个group的size分别为k1和k2,那么我们一直分下去,可以一直分到base case的情况,也就是说size为k1的group需要k1-1次swap,size为k2的grouo |
| 54 | +需要k2-1次swap,那么将整个size为k的group全部放到正确的位置上所需要的swap次数就是```1 + (k1-1) + (k2-1)```,也就是```k1+k2-1``` |
| 55 | +,而其中```k1 + k2 = k```,所以对于Induction case来说也满足这个论点 |
| 56 | + |
| 57 | +证毕 |
| 58 | + |
| 59 | +那么对于我们的N对couple来说,其实就是N个node,我们可以把作为input的row看成一个大家庭,其中有几个group我们可以通过并查集或者dfs算出来,设为m, |
| 60 | +每做一次swap我们就可以增加一个group,最终我们的目的就是形成N个group,即N对couple,所以我们一共需要N-m次swap |
| 61 | + |
| 62 | +并查集,beats 34.83% |
| 63 | + |
| 64 | +```python |
| 65 | +class UnionFind(object): |
| 66 | + def __init__(self, n): # 初始化uf数组和组数目 |
| 67 | + self.count = n |
| 68 | + self.uf = [i for i in range(n)] |
| 69 | + |
| 70 | + def find(self, x): # 判断节点所属于的组 |
| 71 | + while x != self.uf[x]: |
| 72 | + self.uf[x] = self.uf[self.uf[x]] |
| 73 | + x = self.uf[x] |
| 74 | + return self.uf[x] |
| 75 | + |
| 76 | + def union(self, x, y): # 连接两个节点 |
| 77 | + x_root = self.find(x) |
| 78 | + y_root = self.find(y) |
| 79 | + if x_root == y_root: |
| 80 | + return |
| 81 | + self.uf[x_root] = y_root |
| 82 | + self.count -= 1 |
| 83 | + |
| 84 | +class Solution: |
| 85 | + def minSwapsCouples(self, row): |
| 86 | + """ |
| 87 | + :type row: List[int] |
| 88 | + :rtype: int |
| 89 | + """ |
| 90 | + N = len(row) // 2 |
| 91 | + uf = UnionFind(N) |
| 92 | + for i in range(N): |
| 93 | + m, n = row[2*i], row[2*i+1] |
| 94 | + uf.union(m // 2, n // 2) # 说明这两对couple,即这两个node相互连接 |
| 95 | + return N - uf.count |
| 96 | +``` |
| 97 | + |
| 98 | + |
| 99 | +> 思路 2 |
| 100 | +******- 时间复杂度: O(N)******- 空间复杂度: O(N)****** |
| 101 | + |
| 102 | +看完上面的思路,发现直接用贪心就可以,就是直接从头到尾遍历,没配对成功就立马配对,执行相应swap操作 |
| 103 | + |
| 104 | +beats 34.83% |
| 105 | + |
| 106 | + |
| 107 | +```python |
| 108 | +class Solution: |
| 109 | + def minSwapsCouples(self, row): |
| 110 | + """ |
| 111 | + :type row: List[int] |
| 112 | + :rtype: int |
| 113 | + """ |
| 114 | + lookup = {} # people: his idx in row |
| 115 | + for idx, ppl in enumerate(row): |
| 116 | + lookup[ppl] = idx |
| 117 | + res = 0 |
| 118 | + for i in range(0, len(row), 2): |
| 119 | + p1 = row[i] |
| 120 | + p2 = p1 + 1 if p1 % 2 == 0 else p1 - 1 # Find p1's couple |
| 121 | + if row[i+1] != p2: # p1's couple isn't next to p1 |
| 122 | + res += 1 |
| 123 | + s = row[i+1] # stranger |
| 124 | + lookup[s], lookup[p2] = lookup[p2], i+1 # swap p2 and cur stranger |
| 125 | + row[lookup[s]], row[i+1] = s, p2 # couple in right idx, Stranger is gone |
| 126 | + return res |
| 127 | +``` |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | + |
| 134 | + |
| 135 | + |
| 136 | + |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | + |
| 141 | + |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | + |
0 commit comments