天天看點

2 Keys Keyboard

題目描述

2 Keys Keyboard

解題思路

題目大意自不必說,至于說解法,這道題目有想過往動态規劃方面去想,但想着想着就被可愛的某婷帶偏了,不過最後還竟另辟蹊徑,通過對數字本身的研究而解出本題。大緻思路如下:

對于n個字元A,如果想要找出最小的複制粘貼步數,為何不從它的因數找起呢?以42為例,42=21*2=7*3*2,也就是說我要先複制初始的一個A,粘貼得到一個A之後,再複制這兩個A,粘貼2次得到6個A,再複制這6個A,粘貼6次就可以得到42個A啦,當然關于這理論的證明我暫時無法給出,不過Accept證明我這個想法是ok的。

C++代碼實作

class Solution {
public:
    int minSteps(int n) {
        int t = ;
        int a[], p = -;;
        while (n != ) {
            while (n % t != ) {
                t++;
            }
            a[++p] = t;
            n = n / t;
        }
        int result = ;
        for (int i = ; i <= p; ++i) {
            result += a[i];
        }
        return result;
    }
};
           

運作結果顯示

2 Keys Keyboard

動态規劃解法

後來又檢視了題目解析,重新構思動态規劃的方法後發現有異曲同工之妙,即令dp[i]表示要想生成i個字元所需要的步數,初始值dp[0]=dp[1]=0;狀态轉移方程為dp[i]=min(dp[i],dp[j]+i/j),i>1,j<i且i%j==0;因為如果i是j的倍數,那麼i可以通過粘貼i/j-1次得到,但還要加上一次指派這j個字元,是以就是dp[j]+i/j.
           

實作代碼如下:

class Solution {
public:
    int minSteps(int n) {
        vector<int> dp(n+,);

        dp[] = dp[] = ;

        for(int i=;i<n+;++i){
            for(int j=;j<i;++j){
                if(i%j==){
                    dp[i] = min(dp[i], dp[j]+i/j);
                }
            }
        }
        return dp[n];
    }
};