Leetcode:No.650 2 Keys Keyboard

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

Copy All: You can copy all the characters present on the notepad (partial copy is not allowed).
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:
The n will be in the range [1, 1000].

翻譯:略

第一反應就是DP卷雕。
DP第一步要確定含義机杜,一般來說都是直指問題鹰服,也就是說設dp[i]是得到i個A所需要的最少步數(shù)。
那么很明顯就有一個關系:在得到dp[i]的前提下嗦玖,我們可以copy然后paste任意次數(shù),也就是說
dp[k*i]=dp[i] + k 随常,其中k是大于1的正整數(shù)掩蛤,copy1次paste k-1次。
連續(xù)copy沒有意義干花,所以最優(yōu)的單元操作一定是一個copy加上1到若干個paste妄帘。這個公式就涵蓋了所有最優(yōu)單元操作。
當然DP還要注意方向池凄,這里很明顯是從小往大了推抡驼,也就是說已知小的來寫大的。所以上面的公式更合適的寫法如下:

dp[i] = dp[j] + i/j

然后是初始值肿仑。按照我們的定義致盟,dp[0]可以不用管,dp[1] = 0尤慰,從2開始計算馏锡。默認值和n相等就好,相當于copy1個A然后paste n-1次的操作伟端。
代碼:

# Time: O(n)
# Space: O(n)
class Solution:
    def dp(self, n):
        dp = [_ for _ in xrange(n + 1)]
        dp[1] = 0
        for i in xrange(1, n + 1):
            for j in reversed(xrange(1, i / 2 + 1)):
                if i % j == 0:
                    dp[i] = dp[j] + i / j
                    break
        return dp[-1]

可以看到杯道,對于j我是從i / 2往回掃的,因為追求copy基數(shù)盡可能大责蝠,而假如j大于i / 2的話怎么樣也不能整除了党巾,所以這里有一點小小的優(yōu)化。

不過霜医,DP解法并不是最優(yōu)的齿拂。
還是回到問題的本質:在最后一個階段,有n/d個A肴敛,然后paste (d-1)次來得到n署海,算上copy總共花費d步。也就是說n必須要被d整除值朋。然后叹侄,那n/d個A又是怎么來的呢?等于又轉到子問題昨登。其實這里還是DP的那個思路趾代。
不過與DP從小往大推不一樣,這里從最后的n往前推丰辣。另外結合了貪婪算法撒强,即希望paste次數(shù)盡可能少禽捆。比如n=1024,我們期望最優(yōu)結果最后是512個A paste 1次而不是1個A paste 1023次飘哨。
參考代碼(原地址):

    public int minSteps(int n) {
        int s = 0;
        for (int d = 2; d <= n; d++) {
            while (n % d == 0) {
                s += d;
                n /= d;
            }
        }
        return s;
    }

d代表divisor胚想,s代表step。因為整除關系是不分先后順序的芽隆,所以d從2開始先把n所有的2因數(shù)掃出來浊服,然后遞增循環(huán)∨哂酰可以想象成從最后逆推的過程牙躺,只不過采用貪婪算法,每次取最小因數(shù)腕扶,此時被paste的A的數(shù)量最多孽拷。
以1024舉例,上一步期望是512半抱,也就是由512 copy paste脓恕,2步;然后再往前推窿侈,期望256炼幔,因為還是能被2整除,依次類推棉磨,很容易知道1024可以由20步產生(到2個A需要2步江掩,之后A的數(shù)目每x2,步數(shù)+2乘瓤,因為1024=2**10环形,所以還需要9*2=18步,總共20步)衙傀。
假如是5的話抬吟,本身是個質數(shù),只能期望1统抬,所以需要5步火本。
至于復雜度,標題說是O(logn)聪建,我怎么看都是O(n)呢钙畔?

不過,這個算法仍然不是最優(yōu)的金麸。
某種意義上來說擎析,看明白這個題的本質之后,這道題就成了數(shù)學題——求n的所有質因數(shù)之和挥下。
為什么揍魂?代碼就是那個意思啊桨醋。假如n被抽取了所有的2因數(shù),那還會有4,6這些因數(shù)在嗎现斋?
很容易反證:假如到中間某個因數(shù)不是質因數(shù)喜最,那么它肯定能被分解,分解的因數(shù)肯定比自己小庄蹋,那么為什么這些因數(shù)早些時候沒有被弄出去呢瞬内?
基于這個理論,上面的算法可以優(yōu)化:既然是質因數(shù)限书,那么大小肯定不能超過其開方遂鹊,也就是說
d * d <= n
亦即:

    public int minSteps(int n) {
        int s = 0;
        for (int d = 2; d * d <= n; d++) {
            while (n % d == 0) {
                s += d;
                n /= d;
            }
        }
        return s + (n == 1 ? 0 : n);
    }

注意因為d到不了n了,所以d=n的情況要單獨拿出來計算蔗包。
假如n是1,不用擔心慧邮,結果是0调限;
假如n是合數(shù),也不用擔心误澳,這時候n所有因數(shù)被掏空耻矮,只能是1,最后加0忆谓;
假如n是質數(shù)裆装,這時候之前并沒有d能夠動n,所以n還是它自己倡缠,加上n哨免。
所以最后返回的是s + (n == 1 ? 0 : n)

順帶貼一個很Hack的方法昙沦,專門針對n的范圍而造的求質因數(shù)和的函數(shù)琢唾,原帖也在上面那個鏈接里面:

public int minSteps(int n) {
    // list of primes that are not greater than SQRT(n) - in this case, n = 1,000, SQRT(n) = 31.6
    int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
    int ans = 0;
    for (int p : primes) {
        while (n % p == 0) {
            ans += p;
            n /= p;
        }
    }
    return ans + (n == 1 ? 0 : n);
}

基本上就是上面代碼的針對版,既然要求質因數(shù)之和盾饮,那么就只遍歷可能的質因數(shù)采桃,很合理。當然還可以計算哪些質數(shù)滿足d*d<=n丘损,不過這也需要額外的計算力普办。

總結

很有意思的一道題。我覺得這樣的題才算好題:可以從多個角度來解決徘钥,而不是只有一種解法衔蹲。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吏饿,隨后出現(xiàn)的幾起案子踪危,更是在濱河造成了極大的恐慌蔬浙,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贞远,死亡現(xiàn)場離奇詭異畴博,居然都是意外死亡,警方通過查閱死者的電腦和手機蓝仲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門俱病,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袱结,你說我怎么就攤上這事亮隙。” “怎么了垢夹?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵溢吻,是天一觀的道長。 經常有香客問我果元,道長促王,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任而晒,我火速辦了婚禮蝇狼,結果婚禮上,老公的妹妹穿的比我還像新娘倡怎。我一直安慰自己迅耘,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布监署。 她就那樣靜靜地躺著颤专,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焦匈。 梳的紋絲不亂的頭發(fā)上血公,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音缓熟,去河邊找鬼累魔。 笑死,一個胖子當著我的面吹牛够滑,可吹牛的內容都是我干的垦写。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼彰触,長吁一口氣:“原來是場噩夢啊……” “哼梯投!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤分蓖,失蹤者是張志新(化名)和其女友劉穎尔艇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體么鹤,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡终娃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒸甜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棠耕。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柠新,靈堂內的尸體忽然破棺而出窍荧,到底是詐尸還是另有隱情,我是刑警寧澤恨憎,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布蕊退,位于F島的核電站,受9級特大地震影響憔恳,放射性物質發(fā)生泄漏咕痛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一喇嘱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧塞栅,春花似錦者铜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砾医,卻和暖如春拿撩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背如蚜。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工压恒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人错邦。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓探赫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撬呢。 傳聞我的和親對象是個殘疾皇子伦吠,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容