題目鏈接
LeetCode 390. 消除遊戲[1]
題目描述
給定一個從 到 排序的整數列表。 首先,從左到右,從第一個數字開始,每隔一個數字進行刪除,直到列表的末尾。 第二步,在剩下的數字中,從右到左,從倒數第一個數字開始,每隔一個數字進行刪除,直到列表開頭。 我們不斷重複這兩步,從左到右和從右到左交替進行,直到只剩下一個數字。 返回長度爲 的列表中,最後剩下的數字。
示例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]
當時我們的方法是通過編號映射來遞歸尋找下一輪存活的人的,那麼這題也可以嘗試用同樣的方法。
我們分奇偶兩種情況來考慮。
如果 ,那麼如上圖所示,第一輪消除完了之後,剩下的數字就是綠色的偶數部分。
接着就要從右往左遞歸地消除了,那我們從右往左給綠色數字重新編號爲 到 ,問題就轉化爲了 個數字的情況下,最後剩餘的數字是幾了。
假設我們用 表示初始時 個數字最後剩下的編號,那麼綠色部分重新編號後最後剩下的數字就是 。但是怎麼將 重新映射回綠色的數字編號呢?
通過觀察我們可以發現,綠色數字整除 ,再加上藍色的映射後的編號,結果一定等於 。所以我們就得到了映射回去的公式:
比如說你求出來 ,也就是藍色部分最後剩下的數字是 ,那麼映射成綠色的編號就是 ,這就是最初的編號了。
如果 ,那麼如上圖所示,只需要在後面加個橙色的 就行了。
但是第一輪的時候它就被消除了,所以綠色的剩下的編號和之前偶數情況沒有任何區別。所以最終的答案也是:
最後發現奇偶情況下,公式其實可以統一起來,用 來替換 就得到了:
代碼
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. 消除遊戲: https://leetcode-cn.com/problems/elimination-game/
[2]
韋陽的博客:【每日算法Day 74】經典面試題:約瑟夫環,我敢打賭你一定不會最後一種方法!: https://godweiyang.com/2020/03/19/leetcode-interview-62/
[3]
知乎專欄:【每日算法Day 74】經典面試題:約瑟夫環,我敢打賭你一定不會最後一種方法!: https://zhuanlan.zhihu.com/p/114391147