leetcode 650 只有两个键的键盘

传送门
####题目介绍
最初记事本上只有一个字符 ‘A’ 。你每次可以对这个记事本进行两种操作:

Copy All(复制全部):复制这个记事本中的所有字符(不允许仅复制部分字符)。
Paste(粘贴):粘贴 上一次 复制的字符。
给你一个数字 n ,你需要使用最少的操作次数,在记事本上输出 恰好 n 个 ‘A’ 。返回能够打印出 n 个 ‘A’ 的最少操作次数。

####题目样例

示例 1:

输入:3
输出:3
解释:
最初, 只有一个字符 'A'。
第 1 步, 使用 Copy All 操作。
第 2 步, 使用 Paste 操作来获得 'AA'。
第 3 步, 使用 Paste 操作来获得 'AAA'。
示例 2:

输入:n = 1
输出:0
 

提示:
1 <= n <= 1000

####题目解析

通过读题我们知道一个长度为n的字符串必然是由上一次操作之前的字符串和剪切板拼接的字符串组成的。

这里我们进行未知量的假设

假设
S k S_k Sk为第k次操作得到的字符串长度
P a s t e k Paste_k Pastek为第k次操作复制的字符长度

因此,对于一个长度为n的字符串假设最小操作次数为k
得,
n n n = P a s t e k + S i − 1 Paste_k+S_{i-1} Pastek+Si1

由题意知,

∃ ! j ∈ [ 0 , i − 1 ] \exists!j\in[0,i-1] !j[0,i1],有 S j = P a s t e i − 1 S_j=Paste_{i-1} Sj=Pastei1

则有
P a s t e k = { S i − 1 , j = = i − 1 P a s t e j + 1 , e l s e Paste_k =\begin{cases} S_{i-1} & ,j == i-1\\\\ Paste_{j+1} & ,else \end{cases} Pastek=Si1Pastej+1,j==i1,else

N N N

= P a s t e k + S i − 1 =Paste_k+S_{i-1} =Pastek+Si1

= ∑ i = j + 1 k P a s t e j + 1 + S j = \sum_{i = j+1}^{k} Paste_{j+1}+S_j =i=j+1kPastej+1+Sj

= ( k − j ) ∗ S j =(k-j)*S_j =(kj)Sj

= ( n / S j ) ∗ S j =(n/S_j)*S_j =(n/Sj)Sj

这里我们发现 S j S_j Sj实际是n的因子
最终我们就得出了次数的关系式

T i m e n = ( n / S j ) + S j Time_n = (n/S_j)+S_j Timen=(n/Sj)+Sj

显然最大因子的次数是比其他因子少的
因为最大因子与数字的倍数是最小的
比如 t i m e 100 time_{100} time100的两个因子10和20
根据公式
t i m e 100 = 100 / 20 + t i m e 20 = 5 + 2 + t i m e 10 time_{100} = 100/20+ time_{20} = 5+2+time_{10} time100=100/20+time20=5+2+time10
t i m e 100 = 100 / 10 + t i m e 10 = 10 + t i m e 10 time_{100} = 100/10+time_{10} = 10+time_{10} time100=100/10+time10=10+time10
可以看出差距极大

算法实现

1.DFS版

class Solution {
    private:
public:
    int minSteps(int n) {
        if(n == 1)
            return 0;
        int &&mid = n>>1;
        int i = mid;
        while(i>1&&n%i)
            i--;
        return n/i+minSteps(i);
    }
    Solution(){
        
    }
};

2.DFS版打表

class Solution {
    private:
    int num[257];//这里只对于重复率高的进行打表,实际257~512那层只进行了一次运算重复率降低因此这里定义的是512
public:
    int minSteps(int n) {
        if(n<257&&num[n]+1)
            return num[n];
        int &&mid = n>>1;
        int i = mid;
        while(i>1&&n%i)
            i--;
        int last;
        if(i<257)
            num[i] = num[i]==-1?minSteps(i):num[i];
        last = i<257?num[i]:minSteps(i);
        if(n<257){
            num[n] = num[n]==-1?n/i+num[i]:num[n];
            return num[n];
        }
        return n/i+last;
    }
    Solution(){
        memset(num,-1,257*sizeof(int));
        num[0] = num[1] = 0;
        num[2] = 2;
        
    }
};

3.BFS版打表
bfs打表这里空间换时间也使用了O(n)的打表预处理时间,实际上降低了平均损耗,但时间上实际是比dfs多了20ms的损耗,也证明了命中率相对较低


class Solution {
    private:
    int num[1010];
public:
    int minSteps(int n) {
        
        return num[n];
    }
    Solution(){
        memset(num,0,1010*sizeof(int));
        num[0] = num[1] = 0;
        num[2] = 2;
        for(int i = 3;i<1010;i++){
            int &&mid = i>>1;
            int j = mid;
            while(j>1&&i%j)
                j--;
            num[i] = num[j]+i/j;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值