算法分析與設計 第三週


題目描述

在這裏插入圖片描述


選題原因

本週學習了圖,因此選擇了圖算法專項練習,之前做了中等難度的題目,較爲輕鬆,因此本次選了一道困難難度的題目。本來選擇了 684.冗餘連接
但是題目本身有歧義:題目要求當出現多條邊可以刪除時,刪除最先出現的邊,而在例子中給的卻是刪除了後出現的邊,在完成代碼後,發現結果總會有一個過不去。(測試樣例中有兩題,一題答案選擇了先出現的邊,而另一個選擇了後出現的邊)。
因此,選了另一題,即情侶牽手


題目分析

怎麼求最短步驟

本題並沒有直接用到有關圖的算法,卻使用了圖的思想。
題目中包含了一條迷惑信息:交換最少的步驟。在最開始考慮的時候我一直在想會不會交換的順序也會導致結果的差異,但是結合圖算法思想,給了我思路的提示:交換座位的時候要按照鏈表一樣,一個接着一個交換。那麼我們就看一下爲什麼這樣交換的步驟是最少的。
如果我們把每相鄰的兩個座位例如0,1這樣可以把情侶結合在一起而不是1,2這樣的)看作是一個節點,而當出現兩個人需要換座位來實現情侶坐在一起時,看作這兩個節點有聯繫,可以通過一條連接在一起。
很容易發現,最後,會構成很多條單鏈。而每一條就是一次交換。(當換過去的人恰好坐在了自己的情侶旁邊則說明到了終點)

爲什麼這樣可以求出最短步驟

首先,我們需要知道兩個規律。

1.一條鏈從不同順序開始會怎麼樣?

當我們單獨的拿出來一條單鏈出來看,如果我們通過不同的節點開始,那麼最後會在哪個節點終止呢?例如:

  • 原單鏈
A
B
C
D
E
  • 新的單鏈(如果從C開始)
C
D
E
A
B

很容易驗證,但是卻不太容易證明事實上,可以加一條首尾相連的虛線,便於理解。數學水平有限,就暫時不解釋爲什麼會這樣。哭哭。

2.如果交換不同兩條鏈上的座位會怎麼樣?

我們來看兩條不相交的鏈。爲了方便理解,我們把與A配對的數字記爲a1 a2,同理,還有b1 b2\c1 c2\d1 d2。(實際上,只需要首、尾、中間塊即可表示,其他的部分都可以在交換的過程中忽略)

A:a1, b1
B: a2, c2
C: c1, b2
D: d1, e1
E: d2, f2
F: f1, e2

按照我們之前的記法,B中的組合應當爲<a2, c2>,而E中的組合應當爲<d2, f2>。現在我們交換B和E,假設交換a2d2。結果會是這樣。

A
E
F
D
B
C

變成了一條單鏈。與原先相比,變成了5條邊,加上置換的那一次,一共需要6次操作。(事實上,這樣無端的交換隻會將多條單鏈合併成一條單鏈:因爲當交換兩條不同的鏈的時候,就會使得本條鏈中的元素交換到別的鏈中,如果我們將每一條鏈看作一個大的元素(事實上,他是可以形成閉環的,可以看作一個強連通部件),當兩個部件建立聯繫了之後,就會形成更大的迴環)


解題思路

1.維護一個數組,裏面記錄了每一個旅客的座位號。(可以根據座位錶轉換而來)用以根據序號查詢旅客座位(在row中的index)

        int max = row.size();
        int pos[max] = {0};
        //存儲每個人座位號
        for (int i = 0; i < max; i++) {
            pos[row[i]] = i;
        }

2.從開始時遍歷,如果當前座位上是情侶,則跳過。(這也對應構造的圖中的單節點鏈表

        int i = 0;
        while (i < max) {
            if (row[i] / 2 == row[i + 1] / 2) {
                i = i + 2;
            } 

3.如果當前座位上的不是情侶,則說明找到了一條單鏈的某個節點,根據第一條性質,可以直接將這一個節點作爲開始節點,開始調整這一條單鏈上的所有座位,規則如下:

  • 讓當前的鄰座和情侶換位置。(沿走)
  • 鄰座到了新地方,以同樣的規則對待自己的鄰座。(繼續走)
  • 如果旁邊就是自己的情侶,則終止。(到達尾部節點)
			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;
            }

結果

在這裏插入圖片描述


源代碼

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;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章