一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
题目说明
你正在玩一个整数游戏。从整数 1 开始,期望得到整数 target 。
在一次行动中,你可以做下述两种操作之一:
递增,将当前整数的值加 1(即, x = x + 1)。 加倍,使当前整数的值翻倍(即,x = 2 * x)。 在整个游戏过程中,你可以使用 递增 操作 任意 次数。但是只能使用 加倍 操作 至多 maxDoubles 次。
给你两个整数 target 和 maxDoubles ,返回从 1 开始得到 target 需要的最少行动次数。
示例 1:
输入:target = 5, maxDoubles = 0 输出:4 解释:一直递增 1 直到得到 target 。
贪心
核心思想:使用贪心算法倒序处理,加倍变成减半,递增变成递减 先消耗掉所有减半次数 maxDoubles,消耗过程中如果是偶数则减半,如果是奇数则递减,每次消耗次数 count 都加1 剩余的操作只能是递减,需要的操作次数为剩余整数减1即 target - 1 最后返回 count + target - 1。
具体步骤: 1. 从target反推回1; 2. 若target是奇数,那么就只能进行减1操作; 3. 若target是偶数,那么就进行除2操作,同时maxDoubles; 4. 当maxDoubles次数用完或者target已经为1时退出循环,若此时target还不为1,那么就只能做减1操作直到target为1。
class Solution {
public int minMoves(int target, int maxDoubles) {
int count = 0;
while (target>1){
int b = target%2;
if (b!=0){
count++;
target--;
}
if (maxDoubles>0){
maxDoubles--;
count++;
target/=2;
}
else{
count+=(target-1);
target=1;
}
}
return count;
}
}
递归
思路:根据是否可以被2整除分情况讨论,判断递归出口。三种情况如下
- 如果 target == 1,则返回 0,代表不用进行操作
- 如果 maxDoubles == 0,则返回 target - 1,因为不能乘只能加,所以返回 target 和 1 的差
- 如果 maxDoubles > 0,代表还有乘二次数,如果 target 为偶数,就直接除,否则减 1 再除
class Solution {
public int minMoves(int target, int maxDoubles) {
if(target == 1 || maxDoubles == 0){
return target - 1;
}
if(target % 2 == 0 && maxDoubles >= 0 && target >= 1)
return minMoves(target / 2, maxDoubles - 1) + 1;
else
return minMoves((target - 1) / 2, maxDoubles - 1) + 2;
}
}
注意
当target为奇数时,说明前一步只能是递增行动,因此通过递归的方式退回1步,即总最小步数=1+minMoves(target-1, maxDoubles);
如果当target为偶数时,前一步操作是加倍时,步数最小,即总最小步数=
1+minMoves(int(target/2), maxDoubles-1)