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 == ) return ;
int res = n;
for (int i = n - ; i > ; --i) {
if (n % i == ) {
res = min(res, minSteps(n / i) + i);
}
}
return res;
}
};

下面这种方法是用 DP 来做的,我们可以看出来,其实就是上面递归解法的迭代形式,思路没有任何区别,参见代码如下:

解法二:

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

上面的解法其实可以做一丢丢的优化,在遍历j的时候,只要发现了第一个能整除i的j,直接可以用 dp[j] + i/j 来更新 dp[i],然后直接 break 掉j的循环,之后的j就不用再考虑了,可能是因为因数越大,其需要的按键数就越少吧,参见代码如下:

解法三:

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

下面我们来看一种省空间的方法,不需要记录每一个中间值,而是通过改变n的值来实时累加结果res,参见代码如下:

解法四:

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

Github 同步地址:

https://github.com/grandyang/leetcode/issues/650

类似题目:

4 Keys Keyboard

Broken Calculator

参考资料:

https://leetcode.com/problems/2-keys-keyboard/

https://leetcode.com/problems/2-keys-keyboard/discuss/105899/Java-DP-Solution

https://leetcode.com/problems/2-keys-keyboard/discuss/105928/JavaC%2B%2B-Clean-Code-with-Explanation-4-lines-No-DP

https://leetcode.com/problems/2-keys-keyboard/discuss/105897/Loop-best-case-log(n)-no-DP-no-extra-space-no-recursion-with-explanation

 

[LeetCode] 650. 2 Keys Keyboard 两键的键盘的更多相关文章

  1. [LeetCode] 651. 4 Keys Keyboard 四键的键盘

    Imagine you have a special keyboard with the following keys: Key 1: (A): Print one 'A' on screen. Ke ...

  2. [LeetCode] 2 Keys Keyboard 两键的键盘

    Initially on a notepad only one character 'A' is present. You can perform two operations on this not ...

  3. [LeetCode] 4 Keys Keyboard 四键的键盘

    Imagine you have a special keyboard with the following keys: Key 1: (A): Print one 'A' on screen. Ke ...

  4. [leetcode] 650. 2 Keys Keyboard (Medium)

    解法一: 暴力DFS搜索,对每一步进行复制还是粘贴的状态进行遍历. 注意剪枝的地方: 1.当前A数量大于目标数量,停止搜索 2.当前剪贴板数字大于等于A数量时,只搜索下一步为粘贴的状态. Runtim ...

  5. LeetCode 650 - 2 Keys Keyboard

    LeetCode 第650题 Initially on a notepad only one character 'A' is present. You can perform two operati ...

  6. LC 650. 2 Keys Keyboard

    Initially on a notepad only one character 'A' is present. You can perform two operations on this not ...

  7. 【LeetCode】650. 2 Keys Keyboard 只有两个键的键盘(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 素数分解 日期 题目地址:https://le ...

  8. 650. 2 Keys Keyboard

    Initially on a notepad only one character 'A' is present. You can perform two operations on this not ...

  9. 650. 2 Keys Keyboard复制粘贴的次数

    [抄题]: Initially on a notepad only one character 'A' is present. You can perform two operations on th ...

随机推荐

  1. 生成 RSA 公钥和私钥的方法

    在使用 RSA 加密算法时,需要使用到一对 公钥 和 私钥,生成 公钥 和 私钥 需要借助 openssl 这款工具,下载这款工具的地址如下: http://slproweb.com/products ...

  2. C# 去除数字中多于的0

    decimal i = decimal.Parse(Console.ReadLine()); Console.WriteLine((i).ToString(")); Console.Writ ...

  3. Java学习——单元测试JUnit

    Java学习——单元测试JUnit 摘要:本文主要介绍了什么是单元测试以及怎么进行单元测试. 部分内容来自以下博客: https://www.cnblogs.com/wxisme/p/4779193. ...

  4. Python【day 10】函数进阶-动态函数

    形参小结 1.位置参数2.默认值参数3.动态参数 1.*args 位置参数的动态传参. 系统会自动的把所有的位置参数聚合成元组 2.**kwargs 关键字参数的动态传参. 系统会自动的把所有的关键字 ...

  5. Git 分支代码管理日记备注

    1〉  Bithucket 创建代码库 2〉  下载克隆代码 Git clone 代码链接 3〉  代码初始化完成之后,切换到代码文件夹 cd 文件夹名 4〉  查看分支情况 Git brach 5〉 ...

  6. redis 配置及编写启动脚本

    #!/bin/sh # # Simple Redis init.d script conceived to work on Linux systems # as it does use of the ...

  7. 章节十四、9-Actions类鼠标悬停、滚动条、拖拽页面上的元素

    一.鼠标悬停 1.在web网站中,有一些页面元素只需要我们将鼠标指针放在上面就会出现被隐藏的下拉框或者其它元素,在自动化的过程中我们使用Actions类对鼠标进行悬停操作. 2.案例演示 packag ...

  8. Window平台下的静默下载并安装软件脚本bat

    一,隐藏命令窗口 当我们运行bat脚本的时候,弹出CMD窗口.如果要隐藏窗口可以在bat脚本开头处写一下代码: @echo off if "%1" == "h" ...

  9. 在VideoFileClip函数中获取“OSError:[WinError 6]句柄无效”

    我正在使用python通过导入moviepy库创建一个程序,但收到以下错误: from moviepy.editor import VideoFileClip white_output = 'vide ...

  10. Django框架(十七)-- CBV源码分析、restful规范、restframework框架

    一.CBV源码分析 1.url层的使用CBV from app01 import views url(r'book/',views.Book.as_view) 2.as_view方法 as_view是 ...