You are playing a game with integers. You start with the integer 1
and you want to reach the integer target
.
In one move, you can either:
x = x + 1
).x = 2 * x
).You can use the increment operation any number of times, however, you can only use the double operation at most maxDoubles
times.
Given the two integers target
and maxDoubles
, return the minimum number of moves needed to reach target
starting with 1
.
Example 1:
Input: target = 5, maxDoubles = 0 Output: 4 Explanation: Keep incrementing by 1 until you reach target.
Example 2:
Input: target = 19, maxDoubles = 2 Output: 7 Explanation: Initially, x = 1 Increment 3 times so x = 4 Double once so x = 8 Increment once so x = 9 Double again so x = 18 Increment once so x = 19
Example 3:
Input: target = 10, maxDoubles = 4 Output: 4 Explanation: Initially, x = 1 Increment once so x = 2 Double once so x = 4 Increment once so x = 5 Double again so x = 10
Constraints:
1 <= target <= 109
0 <= maxDoubles <= 100
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for finding the minimum moves involves exploring every possible sequence of actions. We simulate performing different operations (either doubling or decrementing) step-by-step, until the target is reached. We then pick the sequence that required the fewest moves.
Here's how the algorithm would work step-by-step:
def minimum_moves_to_reach_target_score_brute_force(
current_score_start,
target_score,
max_doubles
):
queue = [(current_score_start, 0)]
minimum_moves = float('inf')
while queue:
current_score, number_of_moves = queue.pop(0)
if current_score == target_score:
minimum_moves = min(minimum_moves, number_of_moves)
continue
if current_score > target_score:
number_of_moves_needed =
number_of_moves + (current_score - target_score)
minimum_moves = min(minimum_moves, number_of_moves_needed)
continue
# Explore doubling, but only if we have doubles left.
if max_doubles > 0:
queue.append((current_score * 2, number_of_moves + 1))
# Always explore decrementing.
queue.append((current_score - 1, number_of_moves + 1))
return minimum_moves
To minimize the moves, we want to work backward from the target. The optimal strategy involves prioritizing division by two whenever possible because it reduces the score much faster than adding one.
Here's how the algorithm would work step-by-step:
def minimum_moves_to_reach_target_score(target_score, max_doubles):
moves_count = 0
while target_score > 1:
if max_doubles > 0 and target_score % 2 == 0:
# Prioritize dividing by two when possible.
target_score //= 2
max_doubles -= 1
moves_count += 1
elif max_doubles == 0:
# When doubles are exhausted, only addition remains.
moves_count += target_score - 1
target_score = 1
else:
# If not divisible by 2, increment to make it so.
target_score -= 1
moves_count += 1
return moves_count
Case | How to Handle |
---|---|
target is 1 | Return 0 immediately as no moves are needed to reach 1. |
maxDoubles is 0 and target is greater than 1 | Only increment operations are possible; return target - 1. |
target is negative | Return -1 or throw an error, as the problem statement implies positive values; clarify assumptions beforehand. |
maxDoubles is negative | Return -1 or throw an error, as maxDoubles should be non-negative; clarify assumptions beforehand. |
target is close to the maximum integer value and maxDoubles is high, causing possible overflow during doubling. | Use long or consider that integer overflow may cause calculation errors, and appropriate measures such as modular arithmetic if possible, need to be implemented. |
target is much larger than maxDoubles | Optimize for the case where decrementing is more efficient by checking `target % 2 == 0` before doubling |
maxDoubles is extremely large compared to target | Even if doubling is allowed, optimize by decrementing until target becomes closer to a power of 2 if appropriate. |
target equals Integer.MAX_VALUE and maxDoubles is also very large | Handle Integer.MAX_VALUE and avoid unnecessary calculations, possibly optimizing decrements instead of doubling where it's more efficient. |