1. 概述
初始时在记事本上只有一个字符 ‘A’。可以在此记事本上每一步可以进行两种操作:
Copy All:复制记事本上所有的字符(不允许部分复制)Paste:粘贴上一次复制的字符。给出一个数字
n,需要在记事本上得到恰好n个 ‘A’, 问最少需要几步。注:n 在范围 [1, 1000] 内
例1:
输入: 3输出: 3解释: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'.例2:
输入: 1输出: 0
2. 解题
2.1 解题思路
- 每个正常输入的数都可以由一个 A 变为 n 个 A
- 要获取 n 个 A,并且是使用最小的步数的时候,可以得出在 m 次操作之后的下一步是粘贴(1步操作),或者是复制后粘贴(2步操作),如下:
- 如果是复制后粘贴,那么这个数一定是n的一半大小;
- 如果是粘贴,那么就有多种可能性了,可能粘贴的数以及连续粘贴的次数时未知的。
- 那么这里需要两个空间来记录状态:
f(n)记录最少步数能得到 n 个 A;o(n)记录得出f(n)时当前复制的 A 的数量。
- 所以按照第二步的推理,可以把 n 分为奇偶数处理:
class Solution { /**
* @param int $n* @return int : the minimum number of steps to get n 'A'*/public function minSteps(int $n): int{if ($n < 0) {return -1;}/** @var array $minStep 需要的A长度为$i时,最小的操作步数 */$minStep[1] = 0;/** @var array $copyNums 当$minStep[$i]存在时, $copyNums[$i]为当前复制的A数量 */$copyNums[1] = 0;for ($i = 2; $i <= $n; $i++) {// 偶数一定可以取到折半的数进行:复制、粘贴if ($i % 2 == 0) {$minStep[$i] = $minStep[$i / 2] + 2;$copyNums[$i] = $i / 2;} else {// 回溯记录的之前复制过的值,进行多次粘贴是否能得到需要的值for ($j = $i - 1; $j > 0; $j--) {$k = 1;while (1) {$value = $j + ($copyNums[$j] * $k);if ($value < $i) {$k++;continue;} elseif ($value > $i) {break;} else {$minStep[$i] = $minStep[$j] + $k;$copyNums[$i] = $copyNums[$j];break 2;}}}}}return $minStep[$n];}
}
$n = 1000; $cls = new Solution(); $cls->minSteps($n); ```
