[LeetCode] 390. Elimination Game 淘汰遊戲

 

There is a list of sorted integers from 1 to n. Starting from left to right, remove the first number and every other number afterward until you reach the end of the list.html

Repeat the previous step again, but this time from right to left, remove the right most number and every other number from the remaining numbers.java

We keep repeating the steps again, alternating left to right and right to left, until a single number remains.git

Find the last number that remains starting with a list of length n.github

Example:編程

Input:
n = 9,
1 2 3 4 5 6 7 8 9
2 4 6 8
2 6
6

Output:
6

 

這道題是 LeetCode 第二次編程比賽的題,然而博主並無作出來,博主用的方法是那種最笨的方法,用一個數組把n個數組都存起來,而後根據循環的奇偶來決定是從左仍是從右刪除,結果不幸超時 TLE 了。後來經過想大神請教和上網搜索,發現這道題用遞歸來作很簡單,用一個 bool 型變量 left2right,爲 true 表示從左往右,爲 false 表示從右往左遍歷。當n爲1時,不論從左往右仍是從右往左都返回1。若是n大於1,且是從左往右的話,返回2倍的對 n/2 的從右往左的遍歷;若是是從右往左的話,稍稍麻煩一些,確定仍是要對 n/2 調用遞歸函數的,可是要分奇偶狀況,若是n爲奇數,返回2倍的對 n/2 的從左往右的遍歷的值;若是n爲偶數,2倍的對 n/2 的從左往右的遍歷的值,再減去1。具體這樣的緣由,博主還在研究中,也不是太清楚:數組

 

解法一:函數

class Solution {
public:
    int lastRemaining(int n) {
        return help(n, true);    
    }
    int help(int n, bool left2right) {
        if (n == 1) return 1;
        if (left2right) {
            return 2 * help(n / 2, false);
        } else {
            return 2 * help(n / 2, true) - 1 + n % 2;
        }
    }
};

 

下面這種方法至關的叼,一行就搞定了簡直喪心病狂啊。第一次從左往右刪除的時候,奇數都被刪掉了,剩下的都是偶數。若是對全部數都除以2,那麼獲得一個1到 n/2 的新數列。下一次從右往左刪出,那麼返回的結果應該是調用遞歸的結果 lastRemaining(n / 2) 在數組1到 n/2 之間的鏡像。何爲鏡像,好比 1, 2, 3, 4 這個數字,2的鏡像就是3, 1的鏡像是4,參見代碼以下:post

 

解法二:this

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

 

下面這種迭代的解法是博主請教另外一位大神的方法,我的感受也很是叼,膜拜大神中,先來看兩個簡單的例子:url

n = 8
1 2 3 4 5 6 7 8
   2    4    6   8
   2          6
               6
      
n = 7      
1 2 3 4 5 6 7
   2    4    6
         4

若是仔細觀察,能夠發現從左往右刪的時候,每次都是刪掉第一個數字,而從右往左刪的時候,則有可能刪掉第一個或者第二個數字,並且每刪一次,數字之間的距離會變爲以前的兩倍。這裏要作的是每次記錄當前數組的第一個數字,並且再經過觀察能夠看出,從右往左刪時,若是剩下的數字個數是偶數個時,刪掉的是第二個數字;若是是奇數個的時候,刪掉的是第一個數字。總結出了上述規律,就能夠寫出代碼以下:

 

解法三:

class Solution {
public:
    int lastRemaining(int n) {
        int step = 1, res = 1;
        while (step * 2 <= n) {
            res += step;
            step *= 2;
            if (step * 2 > n) break;
            if ((n / step) % 2 == 1) res += step;
            step *= 2;
        }
        return res;
    }
};

 

再來看一種論壇上的高分解法,其實這種解法的本質跟上面那種解法同樣的,這裏多使用了兩個變量,一個是布爾型變量 left2right,表示當前的方向,爲 true 表示是從左往右刪;另外一個是整型變量 remain,表示當前還剩下的數字個數。當 remain 大於1的時候進行循環,res 表示的是當前剩下的左數第一個數字。根據以前的分析,當從左往右刪除的時候,左邊第一個數字必定會被刪掉;而從右往左刪時,若是剩下的數字個數是偶數個時,刪掉的是第二個數字;若是是奇數個的時候,刪掉的是第一個數字。這樣只要判斷 left2right 爲 true,或者 remain 是奇數的時候,res 要加上 step,也就是當前數字之間的間隔數,每刪除一次,step 都要自乘以2,同時 remain 要除以2,left2right 也要變成其相反的狀態,參見代碼以下:

 

解法四:

class Solution {
public:
    int lastRemaining(int n) {
        bool left2right = true;
        int res = 1, step = 1, remain = n;
        while (remain > 1) {
            if (left2right || remain % 2 == 1) res += step;
            remain /= 2;
            step *= 2;
            left2right = !left2right;
        }
        return res;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/390

 

相似題目:

https://leetcode.com/problems/elimination-game/

https://leetcode.com/problems/elimination-game/discuss/87128/C-1-line-solution-with-explanation

https://leetcode.com/problems/elimination-game/discuss/87121/O(logN)-solution.-clear-break-down

https://leetcode.com/problems/elimination-game/discuss/87120/one-line-java-solution-based-on-Josephus-Problem

https://leetcode.com/problems/elimination-game/discuss/87119/JAVA%3A-Easiest-solution-O(logN)-with-explanation

 

LeetCode All in One 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索