[LeetCode] Bulb Switcher II 燈泡開關之二


 

There is a room with n lights which are turned on initially and 4 buttons on the wall. After performing exactly m unknown operations towards buttons, you need to return how many different kinds of status of the n lights could be.

Suppose n lights are labeled as number [1, 2, 3 ..., n], function of these 4 buttons are given below:

  1. Flip all the lights.
  2. Flip lights with even numbers.
  3. Flip lights with odd numbers.
  4. Flip lights with (3k + 1) numbers, k = 0, 1, 2, ...

 

Example 1:

Input: n = 1, m = 1.
Output: 2
Explanation: Status can be: [on], [off]

 

Example 2:

Input: n = 2, m = 1.
Output: 3
Explanation: Status can be: [on, off], [off, on], [off, off]

 

Example 3:

Input: n = 3, m = 1.
Output: 4
Explanation: Status can be: [off, on, off], [on, off, on], [off, off, off], [off, on, on].

 

Note: n and m both fit in range [0, 1000].

 

這道題是之前那道Bulb Switcher的拓展,但是關燈的方式改變了。現在有四種關燈方法,全關,關偶數燈,關奇數燈,關3k+1的燈。現在給我們n盞燈,允許m步操作,問我們總共能組成多少種不同的狀態。博主開始想,題目沒有讓列出所有的情況,而只是讓返回總個數。那么博主覺得應該不能用遞歸的暴力破解來做,一般都是用DP來做啊。可是想了半天也沒想出遞推公式,只得作罷。只好去參考大神們的做法,發現這道題的結果並不會是一個超大數,最多情況只有8種。轉念一想,也是,如果結果是一個超大數,一般都會對一個超大數10e7來取余,而這道題並沒有,所以是一個很大的hint,只不過博主沒有get到。博主應該多列幾種情況的,說不定就能找出規律。下面先來看一種暴力解法,首先我們先做一個小小的優化,我們來分析四種情況:

第一種情況:123456789101112131415,...

第二種情況:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,...

第三種情況:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,...

第四種情況:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,...

通過觀察上面的數組,我們可以發現以6個為1組,都是重復的pattern,那么實際上我們可以把重復的pattern去掉而且並不會影響結果。如果n大於6,我們則對其取余再加上6,新的n跟使用原來的n會得到同樣的結果,但這樣降低了我們的計算量。

下面我們先來生成n個1,這里1表示燈亮,0表示燈滅,然后我們需要一個set來記錄已經存在的狀態,用一個queue來輔助我們的BFS運算。我們需要循環m次,因為要操作m次,每次開始循環之前,先統計出此時queue中數字的個數len,然后進行len次循環,這就像二叉樹中的層序遍歷,必須上一層的結點全部遍歷完了才能進入下一層,當然,在每一層開始前,我們都需要情況集合s,這樣每個操作之間才不會互相干擾。然后在每層的數字循環中,我們取出隊首狀態,然后分別調用四種方法,突然感覺,這很像迷宮遍歷問題,上下左右四個方向,周圍四個狀態算出來,我們將不再集合set中的狀態加入queue和集合set。當m次操作遍歷完成后,隊列queue中狀態的個數即為所求,參見代碼如下:

 

解法一:

class Solution {
public:
    int flipLights(int n, int m) {
        n == (n <= 6) ? n : (n % 6 + 6);
        int start = (1 << n) - 1;
        unordered_set<int> s;
        queue<int> q{{start}};
        for (int i = 0; i < m; ++i) {
            int len = q.size();
            s.clear();
            for (int k = 0; k < len; ++k) {
                int t = q.front(); q.pop();
                vector<int> next{flipAll(t, n), flipEven(t, n), flipOdd(t, n), flip3k1(t, n)};
                for (int num : next) {
                    if (s.count(num)) continue;
                    q.push(num);
                    s.insert(num);
                }
            }
        }
        return q.size();
    }
    
    int flipAll(int t, int n) {
        int x = (1 << n) - 1;
        return t ^ x;
    }
    
    int flipEven(int t, int n) {
        for (int i = 0; i < n; i += 2) {
            t ^= (1 << i);
        }
        return t;
    }
    
    int flipOdd(int t, int n) {
        for (int i = 1; i < n; i += 2) {
            t ^= (1 << i);
        }
        return t;
    }
    
    int flip3k1(int t, int n) {
        for (int i = 0; i < n; i += 3) {
            t ^= (1 << i);
        }
        return t;
    }
};

 

上面那個方法雖然正確,但是有些復雜了,由於這道題最多只有8中情況,所以很適合分情況來討論:

- 當m和n其中有任意一個數是0時,返回1

- 當n = 1時

只有兩種情況,0和1

- 當n = 2時,

這時候要看m的次數,如果m = 1,那么有三種狀態 00,01,10

當m > 1時,那么有四種狀態,00,01,10,11

- 當m = 1時,

此時n至少為3,那么我們有四種狀態,000,010,101,011

- 當m = 2時,

此時n至少為3,我們有七種狀態:111,101,010,100,000,001,110

- 當m > 2時,

此時n至少為3,我們有八種狀態:111,101,010,100,000,001,110,011

 

解法二:

class Solution {
public:
    int flipLights(int n, int m) {
        if (n == 0 || m == 0) return 1;
        if (n == 1) return 2;
        if (n == 2) return m == 1 ? 3 : 4;
        if (m == 1) return 4;
        return m == 2 ? 7 : 8;
    }
};

 

下面這種簡潔到變態的方法是史蒂芬大神觀察規律得到的,他自己也在帖子中說不清為啥這樣可以,但是就是叼,貼上來純屬娛樂吧~

 

解法三:

class Solution {
public:
    int flipLights(int n, int m) {
        n = min(n, 3);
        return min(1 << n, 1 + m * n);
    }
};

 

類似題目:

Bulb Switcher

 

參考資料:

https://discuss.leetcode.com/topic/102022/c-concise-code-o-1

https://discuss.leetcode.com/topic/102395/2-short-lines-simple-formula

https://discuss.leetcode.com/topic/102227/short-and-clean-java-o-1-solution

https://discuss.leetcode.com/topic/102107/easy-to-understand-java-bfs-solution-o-m

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM