1. 程式人生 > >[LeetCode] 2 Keys Keyboard 兩鍵的鍵盤

[LeetCode] 2 Keys Keyboard 兩鍵的鍵盤

post 聯系 not 註意 quest ted 不能 vector ...

Initially on a notepad only one character ‘A‘ is present. You can perform two operations on this notepad for each step:

  1. Copy All: You can copy all the characters present on the notepad (partial copy is not allowed).
  2. Paste: You can paste the characters which are copied last time.

Given a number n

. You have to get exactly n ‘A‘ on the notepad by performing the minimum number of steps permitted. Output the minimum number of steps to get n ‘A‘.

Example 1:

Input: 3
Output: 3
Explanation:
Intitally, we have one character ‘A‘.
In step 1, we use Copy All operation.
In step 2, we use Paste operation to get ‘AA‘.
In step 3, we use Paste operation to get ‘AAA‘.

Note:

  1. The n will be in the range [1, 1000].

這道題只給了我們兩個按鍵,如果只能選擇兩個按鍵,那麽博主一定會要復制和粘貼,此二鍵在手,天下我有!!!果然,這道題就是給了我們復制和粘貼這兩個按鍵,然後給了我們了一個A,我們的目標時利用這兩個鍵來打印出n個A,註意復制的時候時全部復制,不能選擇部分來復制,然後復制和粘貼都算操作步驟,問我們打印出n個A需要多少步操作。對於這種有明顯的遞推特征的題,我們要有隱約的感覺,一定要嘗試遞歸和DP。遞歸解法一般接近於暴力搜索,但是有時候是可以優化的,從而能夠通過OJ。而一旦遞歸不行的話,那麽一般來說DP這個大殺器都能解的。還有一點,對於這種題,找規律最重要,DP要找出遞推公式,而如果無法發現內在的聯系,那麽遞推公式就比較難寫出來了。所以,我們需要從簡單的例子開始分析,試圖找規律:

當n = 1時,已經有一個A了,我們不需要其他操作,返回0

當n = 2時,我們需要復制一次,粘貼一次,返回2

當n = 3時,我們需要復制一次,粘貼兩次,返回3

當n = 4時,這就有兩種做法,一種是我們需要復制一次,粘貼三次,共4步,另一種是先復制一次,粘貼一次,得到AA,然後再復制一次,粘貼一次,得到AAAA,兩種方法都是返回4

當n = 5時,我們需要復制一次,粘貼四次,返回5

當n = 6時,我們需要復制一次,粘貼兩次,得到AAA,再復制一次,粘貼一次,得到AAAAAA,共5步,返回5

通過分析上面這6個簡單的例子,我想我們已經可以總結出一些規律了,首先對於任意一個n(除了1以外),我們最差的情況就是用n步,不會再多於n步,但是有可能是會小於n步的,比如n=6時,就只用了5步,仔細分析一下,發現時先拼成了AAA,再復制粘貼成了AAAAAA。那麽什麽情況下可以利用這種方法來減少步驟呢,分析發現,小模塊的長度必須要能整除n,這樣才能拆分。對於n=6,我們其實還可先拼出AA,然後再復制一次,粘貼兩次,得到的還是5。分析到這裏,我想解題的思路應該比較清晰了,我們要找出n的所有因子,然後這個因子可以當作模塊的個數,我們再算出模塊的長度n/i,調用遞歸,加上模塊的個數i來更新結果res即可,參見代碼如下:

解法一:

class Solution {
public:
    int minSteps(int n) {
        if (n == 1) return 0;
        int res = n;
        for (int i = n - 1; i > 1; --i) {
            if (n % i == 0) {
                res = min(res, minSteps(n / i) + i);
            }
        }
        return res;
    }
};

下面這種方法是用DP來做的,我們可以看出來,其實就是上面遞歸解法的叠代形式,思路沒有任何區別,參見代碼如下:

解法二:

class Solution {
public:
    int minSteps(int n) {
        vector<int> dp(n + 1, 0);
        for (int i = 2; i <= n; ++i) {
            dp[i] = i;
            for (int j = i - 1; j > 1; --j) {
                if (i % j == 0) {
                    dp[i] = min(dp[i], dp[j] + i / j);
                }
            }
        }
        return dp[n];
    }
};

下面我們來看一種省空間的方法,我們不需要記錄每一個中間值,而是通過改變n的值來實時累加結果res,參見代碼如下:

解法三:

class Solution {
public:
    int minSteps(int n) {
        int res = 0;
        for (int i = 2; i <= n; ++i) {
            while (n % i == 0) {
                res += i;
                n /= i;
            }
        }
        return res;
    }
};

參考資料:

https://discuss.leetcode.com/topic/97590/java-dp-solution

https://discuss.leetcode.com/topic/97623/loop-best-case-log-n-no-dp-no-extra-space-no-recursion-with-explanation

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

[LeetCode] 2 Keys Keyboard 兩鍵的鍵盤