思路
給定一個大小為 個陣列,裡面擺放著 的數字,現在,0,1是一對情侶,2,3是一對情侶,找出要將位置倆倆交換幾次,才能讓所有情侶都坐在彼此的隔壁?
假如現在的陣列是 。
沒有滿足條件的情侶對有 。
此時交換 ,就能讓所有情侶在彼此隔壁,共交換兩次。
我們先為「情侶對」去編號。對於 來說,它屬於第 對情侶。
然後按照位置,將「情侶對」的編號合併到一個集合當中。
在一個集合當中,如果存在 對情侶,就需要 次交換,讓集合中的情侶都成對。
拿上面的例子來說,
- 前兩個位置 分別對應第 3, 0 對情侶,將這兩個編號合併到相同集合。
- 對應相同編號 4 ,合併後保持原狀。
- 對應 0, 3, 合併到相同集合。
- 對應編號 1, 2,合併到相同集合
- 對應 2, 1, 合併到相同集合。 最後合併的集合為 , 大小分別是 2, 2, 1, 因此最後答案是 。
再舉一個例子: ,一共五對情侶。
- 合併
- 合併
- 合併
- 合併
- 合併
最後合併的集合為 ,
大小分別是 4, 1, 因此最後答案是 。
第一個比較大的集合,對應到 編號的人,交換次數為 3,
以下是實際交換的方式,用 0 去出走,拯救世界。
- 交換 :
- 交換 :
- 交換 :
每一次,0 都會為坐自己旁邊的人找到對象。
如果集合的大小為 ,那麼有 對情侶沒有配對正確,此時就讓一個人出走拯救世界,
當拯救完 對情侶時,自己也抵達了終點,配到了人,所以是 次。
因此用並查集去合併這些集合,記錄這些集合的大小,比如現在有三個集合,
大小為 ,一共需要交換 次,
而我們知道總共有 對情侶,所以 。
答案就是 ,3 代表集合的數量。
程式碼
並查集
class Solution {
private:
static const int MX = 31;
int parent[MX];
int setCnt; // 紀錄一共有多少集合
int find(int x) {
if(x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if(rootX != rootY) {
parent[rootX] = rootY;
setCnt--; // 合併時更新集合數量
}
}
public:
int minSwapsCouples(vector<int>& row) {
int n = row.size();
// 初始化並查集
iota(parent, parent + n / 2, 0);
setCnt = n / 2;
// 將兩兩「情侶對編號」合併
for(int i = 0; i < n; i += 2) {
unite(row[i] / 2, row[i + 1] / 2);
}
return n / 2 - setCnt;
}
};
複雜度分析
- 時間複雜度:
- 空間複雜度: