【每日算法Day 85】圖解算法:一行代碼解決約瑟夫環的變體

題目鏈接

LeetCode 390. 消除遊戲[1]

題目描述

給定一個從 1n 排序的整數列表。 首先,從左到右,從第一個數字開始,每隔一個數字進行刪除,直到列表的末尾。 第二步,在剩下的數字中,從右到左,從倒數第一個數字開始,每隔一個數字進行刪除,直到列表開頭。 我們不斷重複這兩步,從左到右和從右到左交替進行,直到只剩下一個數字。 返回長度爲 n 的列表中,最後剩下的數字。

示例1

        輸入:
n = 9,
1 2 3 4 5 6 7 8 9
2 4 6 8
2 6
6
輸出:
6

      

題解

還記得幾天前講過的約瑟夫環問題嗎?不記得了就回顧一下吧:

韋陽的博客:【每日算法Day 74】經典面試題:約瑟夫環,我敢打賭你一定不會最後一種方法![2]

知乎專欄:【每日算法Day 74】經典面試題:約瑟夫環,我敢打賭你一定不會最後一種方法![3]

當時我們的方法是通過編號映射來遞歸尋找下一輪存活的人的,那麼這題也可以嘗試用同樣的方法。

我們分奇偶兩種情況來考慮。

v2-18c5b0e8c503a39bf2d6f3f7fd9c0f19_b.jpg

如果 n=2k ,那麼如上圖所示,第一輪消除完了之後,剩下的數字就是綠色的偶數部分。

接着就要從右往左遞歸地消除了,那我們從右往左給綠色數字重新編號爲 1k ,問題就轉化爲了 k 個數字的情況下,最後剩餘的數字是幾了。

假設我們用 f(2k) 表示初始時 n=2k 個數字最後剩下的編號,那麼綠色部分重新編號後最後剩下的數字就是 f(k) 。但是怎麼將 f(k) 重新映射回綠色的數字編號呢?

通過觀察我們可以發現,綠色數字整除 2 ,再加上藍色的映射後的編號,結果一定等於 k+1 。所以我們就得到了映射回去的公式:

f(2k) = 2(k+1-f(k)) \\

比如說你求出來 f(k) = 2 ,也就是藍色部分最後剩下的數字是 2 ,那麼映射成綠色的編號就是 2k-2 ,這就是最初的編號了。

v2-4e3dbc6a694b7e6b614140d2cdaccba3_b.jpg

如果 n=2k+1 ,那麼如上圖所示,只需要在後面加個橙色的 2k+1 就行了。

但是第一輪的時候它就被消除了,所以綠色的剩下的編號和之前偶數情況沒有任何區別。所以最終的答案也是:

f(2k+1) = 2(k+1-f(k)) \\

最後發現奇偶情況下,公式其實可以統一起來,用 n 來替換 k 就得到了:

f(n) = 2\left(\left\lfloor\frac{n}{2}\right\rfloor-f\left(\left\lfloor\frac{n}{2}\right\rfloor\right)\right) \\

代碼

c++

        class Solution {
public:
    int lastRemaining(int n) {
        return n==1 ? 1 : 2*(n/2+1-lastRemaining(n/2));
    }
};

      

python

        class Solution:
    def lastRemaining(self, n: int) -> int:
        return 1 if n==1 else 2*(n//2+1-self.lastRemaining(n//2))
      

參考資料

[1]

LeetCode 390. 消除遊戲: leetcode-cn.com/problem

[2]

韋陽的博客:【每日算法Day 74】經典面試題:約瑟夫環,我敢打賭你一定不會最後一種方法!: godweiyang.com/2020/03/

[3]

知乎專欄:【每日算法Day 74】經典面試題:約瑟夫環,我敢打賭你一定不會最後一種方法!: zhuanlan.zhihu.com/p/11

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章