題目描述
選題原因
之前兩週做了兩道
中等
難度的題目,結果還算不錯,因此這次準備選一道困難
的題目。篩選了圖算法
,開始選了一道冗餘連接II
,完成之後發現題目有一些bug
。當選擇的時候,題目要求有多條邊冗餘的時候需要選擇先出現的邊,但是實際測試的時候並不是以這樣的標準。因此放棄了冗餘連接II
,重新選擇了這一題情侶牽手
。
題目分析
突然一看,這一題彷彿和
圖算法
沒有什麼關係。但是實際上這道題的核心就是有環圖
。
我們先來看看這一題怎麼解。在觀察這道題的時候不難會產生疑問:當我們交換的座位順序不同的時候會不會對結果有影響?這樣得到的結果還是最少次數嗎?
但是結合有環圖
的思想來看,最優的解法就是不停的讓一個人離開,去找她的伴侶。
想要理解,我們需要用有環圖
的思想來類比。
我們把每兩個相鄰的座位看作一個節點
(0,1或2,3這樣的),而如果兩個座位換了的話,就看做兩個節點有聯繫,我們就用一條邊
來把他們連接起來。
下面,我們需要理解兩個思想:
- 一個人出去找座位開始,直到找到的剛好是自己的情侶爲終止,可以構成一條單鏈。
- 相鄰兩條單鏈如果連線,會使步驟增加。
從一條單鏈任意節點開始,最後需要的步數相同。
我們還是按照原有的思想,先找出一條單鏈。
若是我們改爲從B節點出發,那麼結果會是這樣
很容易發現,只要是從這一條路上開始,無論哪一個節點,最後都會使用相同的步驟到達終點。(可以試着構造一條虛環,便於理解)
相鄰兩條單鏈連線會使得消耗更多的步驟
這一條理解起來就有些困難了,那就讓我們引入一些概念:假設A節點一對情侶爲a1, a2; 同理,B節點情侶爲b1, b2。
讓我們構造兩條不相關的單鏈。
如果我們將他們首尾相連,交換他們的元素,那麼會發生什麼情況?
我們驚奇的發現,他們連成了一條線?爲什麼會這樣呢?其實很容易理解:我們將節點裏的每一個元素看成一個連接因子,當兩條鏈的連接因子交換了之後,兩條鏈就必然產生聯繫;而每一條原本都是獨立的,當一個因子交換出去了之後,也只會有一個元素需要和外界連接,如此一來,就會構成一條鏈。
但這樣,步數增加了多少?2
步。
如果我們交換中間節點呢?
我們發現,又構成了一條鏈?其實,也不是所有情況都會構成一條鏈,有時也會是交錯的連接。但是無論怎樣,步驟都會增加。
解題思路
- 準備一個數組將存儲進每一個人對應的座位號
- 從開頭開始遍歷,每兩位檢驗一次,如果是情侶則跳過,否則就需要進入換座位的循環
- 找到情侶所在的位置,將鄰座和情侶互換
- 換走的鄰居到達了新地方,查找自己情侶的位置,讓鄰座和自己的情侶換座位
若換到新座位,旁邊恰好是自己的情侶,則終止。
核心代碼部分
計算座位號
//存儲每個人座位號
for (int i = 0; i < max; i++) {
pos[row[i]] = i;
}
換座位
int lover_site = pos[lover];
//當前旁邊旅客序號及座位號
int beside = row[i + 1];
int beside_site = pos[beside];
//每次配對,讓當前座位的情侶過來,與旁邊的人交換,旁邊的人交換過去後,再次循環
//如果交換過去的人旁邊就是自己的情侶,則循環結束
while (my_site / 2 != lover_site / 2) {
sum++;
row[beside_site] = lover; //情侶過來
pos[beside] = lover_site;
row[lover_site] = beside; //原座位走開
pos[lover] = beside_site;
//計算走了的旅客現在的情侶,旁邊旅客序號、座位號,並再次循環
me = beside;
my_site = pos[me];
if (me % 2 == 0) {
lover = me + 1;
} else {
lover = me - 1;
}
lover_site = pos[lover];
if (my_site % 2 == 0) {
beside = row[my_site + 1];
} else {
beside = row[my_site - 1];
}
beside_site = pos[beside];
}
源代碼
class Solution {
public:
int minSwapsCouples(vector<int>& row) {
int max = row.size();
int pos[max] = {0};
//存儲每個人座位號
for (int i = 0; i < max; i++) {
pos[row[i]] = i;
}
int sum = 0;
int i = 0;
while (i < max) {
//座位上是一對情侶
if (row[i] / 2 == row[i + 1] / 2) {
i = i + 2;
} else {
//當前序號及座位號
int me = row[i];
int my_site = pos[i];
//情侶序號及座位號
int lover;
if (me % 2 == 0) {
lover = me + 1;
} else {
lover = me - 1;
}
int lover_site = pos[lover];
//當前旁邊旅客序號及座位號
int beside = row[i + 1];
int beside_site = pos[beside];
//每次配對,讓當前座位的情侶過來,與旁邊的人交換,旁邊的人交換過去後,再次循環
//如果交換過去的人旁邊就是自己的情侶,則循環結束
while (my_site / 2 != lover_site / 2) {
sum++;
row[beside_site] = lover; //情侶過來
pos[beside] = lover_site;
row[lover_site] = beside; //原座位走開
pos[lover] = beside_site;
//計算走了的旅客現在的情侶,旁邊旅客序號、座位號,並再次循環
me = beside;
my_site = pos[me];
if (me % 2 == 0) {
lover = me + 1;
} else {
lover = me - 1;
}
lover_site = pos[lover];
if (my_site % 2 == 0) {
beside = row[my_site + 1];
} else {
beside = row[my_site - 1];
}
beside_site = pos[beside];
}
i = i + 2;
}
}
return sum;
}
};