You are given two integers, target
and maxDoubles
. Your goal is to reach the target
score starting from 1
, using the minimum number of moves. In each move, you can:
1
(as many times as you want).maxDoubles
times in total).
Find the minimum number of moves required to reach target
from 1
. Each operation counts as one move. You cannot use more than maxDoubles
double operations.
Constraints:
maxDoubles
.1 <= target <= 10^9
0 <= maxDoubles <= 100
At first glance, the problem seems to encourage a brute-force approach: starting from 1
, simulate every possible sequence of increments and doubles until you reach target
. However, this would be extremely inefficient for large values of target
.
The key insight is to realize that, since doubling multiplies your score by 2, it is much more powerful than incrementing by 1. Therefore, you want to use the double operation as much as possible, but only when it is most effective. Also, since you can only double a limited number of times (maxDoubles
), you need to be strategic about when to use it.
Instead of building up from 1
to target
, it is often easier to think in reverse: start from target
and work backwards to 1
, deciding whether the previous move was an increment or a double. This reverse thinking helps avoid unnecessary operations and makes the greedy strategy clear.
We use a greedy, reverse approach to minimize the number of moves:
target
to 1
: At each step, decide whether the previous operation was an increment or a double.
maxDoubles > 0
):
target
is even, the last operation could have been a double. Divide target
by 2 and use one double operation.target
is odd, the last operation must have been an increment. Subtract 1 from target
.target - 1
(since you start at 1).target
becomes 1.
This approach is efficient because it always chooses the operation that brings target
closer to 1
in the fewest possible moves, using doubles only when allowed and beneficial.
Example: target = 10
, maxDoubles = 4
target = 10
, maxDoubles = 4
, moves = 0
maxDoubles > 0
: double used, target = 5
, maxDoubles = 3
, moves = 1
target = 4
, moves = 2
maxDoubles > 0
: double used, target = 2
, maxDoubles = 2
, moves = 3
maxDoubles > 0
: double used, target = 1
, maxDoubles = 1
, moves = 4
target = 1
: done.Result: 4 moves are required.
Brute-force approach: Simulating all possible sequences from 1 to target
would take exponential time, which is infeasible for large target
.
Optimized (greedy, reverse) approach:
target
by at least half (when doubling) or by 1 (when incrementing).O(log(target))
for doubles, plus O(target)
for increments if maxDoubles = 0
. But since maxDoubles
is small (≤ 100), the loop is very fast.O(1)
because we only use a few variables.The problem is efficiently solved by thinking in reverse and greedily using double operations whenever possible. This approach minimizes the number of moves by leveraging the power of doubling, and only using increments when necessary. The solution is both efficient and elegant, requiring only a simple loop and constant space.
class Solution:
def minMoves(self, target: int, maxDoubles: int) -> int:
moves = 0
while target > 1 and maxDoubles > 0:
if target % 2 == 0:
target //= 2
maxDoubles -= 1
else:
target -= 1
moves += 1
moves += target - 1
return moves
class Solution {
public:
int minMoves(int target, int maxDoubles) {
int moves = 0;
while (target > 1 && maxDoubles > 0) {
if (target % 2 == 0) {
target /= 2;
maxDoubles--;
} else {
target -= 1;
}
moves++;
}
moves += target - 1;
return moves;
}
};
class Solution {
public int minMoves(int target, int maxDoubles) {
int moves = 0;
while (target > 1 && maxDoubles > 0) {
if (target % 2 == 0) {
target /= 2;
maxDoubles--;
} else {
target -= 1;
}
moves++;
}
moves += target - 1;
return moves;
}
}
var minMoves = function(target, maxDoubles) {
let moves = 0;
while (target > 1 && maxDoubles > 0) {
if (target % 2 === 0) {
target = Math.floor(target / 2);
maxDoubles--;
} else {
target -= 1;
}
moves++;
}
moves += target - 1;
return moves;
};