991. Broken Calculator
Problem Description
You have a broken calculator that initially displays the integer startValue
. The calculator can only perform two operations:
- Multiply by 2: Multiply the current number on the display by 2
- Subtract 1: Subtract 1 from the current number on the display
Given two integers startValue
and target
, you need to find the minimum number of operations required to transform the initial value startValue
into the target
value.
For example, if startValue = 2
and target = 3
, you could:
- Multiply 2 by 2 to get 4 (1 operation)
- Subtract 1 to get 3 (1 operation)
- Total: 2 operations
The goal is to determine the optimal sequence of these two operations that will get you from startValue
to target
using the fewest number of steps possible.
Intuition
The key insight is to think about this problem in reverse. Instead of going from startValue
to target
, we work backwards from target
to startValue
.
Why does this help? Consider the two operations we can perform forward:
- Multiply by 2
- Subtract 1
When we reverse these operations, they become:
- Divide by 2 (reverse of multiply by 2)
- Add 1 (reverse of subtract 1)
Working backwards is more efficient because:
-
When
target
is larger thanstartValue
, going forward would require many multiply operations, but we might overshoot and need to subtract. It's hard to predict the optimal path. -
Working backwards gives us clear rules:
- If
target
is even, we should divide by 2 (since this was a multiply operation going forward) - If
target
is odd, we can't divide by 2, so we must add 1 first to make it even
- If
This greedy approach works because dividing by 2 reduces the number much faster than adding 1. So whenever we can divide (when the number is even), we should do it.
The process continues until target
becomes less than or equal to startValue
. At that point, we can only use subtraction operations going forward (since multiplication would increase the value). The remaining operations needed are simply startValue - target
.
For example, with startValue = 2
and target = 3
:
- Start with 3 (odd), add 1 → 4 (1 operation)
- 4 is even, divide by 2 → 2 (1 operation)
- Now 2 equals
startValue
, so we're done with 2 operations
Solution Approach
The implementation uses a reverse calculation method, working backwards from target
to startValue
.
Here's how the algorithm works step by step:
-
Initialize a counter: Start with
ans = 0
to count the number of operations. -
Main loop: While
startValue < target
, we perform reverse operations ontarget
:- Check if
target
is odd: We use bitwise AND (target & 1
) to check if the least significant bit is 1, which indicates an odd number - If odd: Add 1 to
target
(target += 1
). This reverses a subtract operation - If even: Divide
target
by 2 using right shift (target >>= 1
). This reverses a multiply operation - Increment the operation counter (
ans += 1
) for each iteration
- Check if
-
Final adjustment: Once
target <= startValue
, we can only reachtarget
fromstartValue
through subtraction operations. AddstartValue - target
to the operation count. -
Return the result: The total number of operations is stored in
ans
.
The algorithm efficiently uses:
- Bitwise operations for performance:
target & 1
checks oddness,target >>= 1
divides by 2 - Greedy approach: Always choosing division when possible (for even numbers) minimizes operations since division reduces the value faster than addition
- Simple arithmetic: The final step handles any remaining difference with direct subtraction count
Time Complexity: O(log target)
- In the worst case, we divide target
by 2 repeatedly until it's less than startValue
Space Complexity: O(1)
- We only use a constant amount of extra space for the counter variable
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with startValue = 5
and target = 8
.
Working backwards from target to startValue:
Initial state: ans = 0
, we need to transform target = 8
to reach startValue = 5
or below.
Step 1: Check if 5 < 8
? Yes, so we continue.
- Is
8
odd? Check8 & 1 = 0
, so it's even. - Since it's even, divide by 2:
8 >> 1 = 4
- Increment counter:
ans = 1
- Current state:
target = 4
,ans = 1
Step 2: Check if 5 < 4
? No, so we exit the loop.
Step 3: Final adjustment:
- Since
target = 4
is now less thanstartValue = 5
, we needstartValue - target = 5 - 4 = 1
more operation - Add to counter:
ans = 1 + 1 = 2
Result: Minimum operations needed = 2
Verification (forward direction):
- Start with 5
- Operation 1: Multiply by 2 → 10
- Operation 2: Subtract 1 → 9
- Operation 3: Subtract 1 → 8
- Wait, that's 3 operations!
Let me recalculate:
- Start with 5
- Operation 1: Subtract 1 → 4
- Operation 2: Multiply by 2 → 8
- Total: 2 operations ✓
The algorithm correctly found that we need to subtract first to get to 4, then multiply by 2 to reach 8, using exactly 2 operations.
Solution Implementation
1class Solution:
2 def brokenCalc(self, startValue: int, target: int) -> int:
3 # Initialize operation counter
4 operations = 0
5
6 # Work backwards from target to startValue
7 # While target is greater than startValue, reduce target
8 while startValue < target:
9 # Check if target is odd using bitwise AND
10 if target & 1: # Equivalent to: if target % 2 == 1
11 # If target is odd, we must have reached it by subtracting 1
12 # So add 1 to reverse that operation
13 target += 1
14 else:
15 # If target is even, we could have reached it by multiplying by 2
16 # So divide by 2 (using right shift for efficiency)
17 target >>= 1 # Equivalent to: target //= 2
18
19 # Increment operation count
20 operations += 1
21
22 # After the loop, target <= startValue
23 # We need (startValue - target) subtractions to reach target
24 operations += startValue - target
25
26 return operations
27
1class Solution {
2 public int brokenCalc(int startValue, int target) {
3 // Count the minimum number of operations needed
4 int operationCount = 0;
5
6 // Work backwards from target to startValue
7 // We reverse the operations: multiply by 2 becomes divide by 2,
8 // and subtract 1 becomes add 1
9 while (startValue < target) {
10 // Check if target is odd using bitwise AND
11 if ((target & 1) == 1) {
12 // If target is odd, we add 1 (reverse of subtract 1)
13 target++;
14 } else {
15 // If target is even, we divide by 2 (reverse of multiply by 2)
16 // Using right shift for division by 2
17 target >>= 1;
18 }
19 // Increment operation count for each step
20 operationCount++;
21 }
22
23 // If startValue >= target after the loop, we need additional subtract operations
24 // Add the difference to account for these remaining operations
25 operationCount += startValue - target;
26
27 return operationCount;
28 }
29}
30
1class Solution {
2public:
3 int brokenCalc(int startValue, int target) {
4 // Count the number of operations needed
5 int operationCount = 0;
6
7 // Work backwards from target to startValue
8 // If we were going forward: multiply by 2 or subtract 1
9 // Going backward: divide by 2 (if even) or add 1 (if odd)
10 while (startValue < target) {
11 // Check if target is odd using bitwise AND
12 if (target & 1) {
13 // If target is odd, we must have arrived here by subtracting 1
14 // So going backward means adding 1
15 target++;
16 } else {
17 // If target is even, we could have arrived here by multiplying by 2
18 // So going backward means dividing by 2 (right shift by 1)
19 target >>= 1;
20 }
21 // Increment operation counter
22 ++operationCount;
23 }
24
25 // If startValue >= target, we can only subtract to reach target
26 // Add the remaining difference to the operation count
27 operationCount += startValue - target;
28
29 return operationCount;
30 }
31};
32
1/**
2 * Calculates the minimum number of operations to transform startValue to target
3 * using two operations: multiply by 2 or subtract 1
4 * We work backwards from target to startValue for efficiency
5 */
6function brokenCalc(startValue: number, target: number): number {
7 let operationCount = 0;
8
9 // Work backwards from target to startValue
10 // Continue while startValue is less than target
11 for (; startValue < target; operationCount++) {
12 // Check if target is odd using bitwise AND
13 if (target & 1) {
14 // If target is odd, we can only reach it by subtracting 1
15 // So we increment target (reverse of subtract 1)
16 target++;
17 } else {
18 // If target is even, we can reach it by multiplying by 2
19 // So we divide target by 2 (reverse of multiply by 2)
20 target >>= 1; // Right shift by 1 is equivalent to dividing by 2
21 }
22 }
23
24 // If startValue >= target, we can only subtract 1 repeatedly
25 // Add the difference to the operation count
26 operationCount += startValue - target;
27
28 return operationCount;
29}
30
Time and Space Complexity
The time complexity is O(log target)
. The algorithm works backwards from target
to startValue
. In each iteration of the while loop, when startValue < target
, we either add 1 to target
(if it's odd) or divide target
by 2 (if it's even). Since division by 2 happens for even numbers, the value of target
decreases exponentially, similar to binary search. The maximum number of iterations is proportional to the number of bits in target
, which is log₂(target)
. After the loop, we perform a constant time operation startValue - target
, so the overall time complexity remains O(log target)
.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space for the variables ans
, startValue
, and target
. No additional data structures that grow with input size are used, and there's no recursion that would consume stack space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting Forward Simulation Instead of Backward
The most common mistake is trying to solve this problem by working forward from startValue
to target
. This approach leads to a complex branching problem where at each step you must decide whether to multiply by 2 or subtract 1, resulting in an exponential search space.
Why it fails: When working forward, you don't know which operation to choose at each step. For example, starting from 2 to reach 3, should you multiply first (2→4) or subtract first (2→1)? This creates a tree of possibilities that's difficult to optimize.
Solution: Always work backward from target
to startValue
. The reverse operations are deterministic:
- If the current number is odd, you must add 1 (reversing a subtraction)
- If the current number is even, divide by 2 (reversing a multiplication)
2. Incorrect Handling of Edge Cases Where startValue >= target
Some implementations forget to handle cases where startValue
is already greater than or equal to target
.
Example pitfall code:
def brokenCalc(self, startValue: int, target: int) -> int:
operations = 0
while target > startValue: # Wrong condition!
if target & 1:
target += 1
else:
target >>= 1
operations += 1
return operations # Missing the final adjustment!
Solution: The correct approach uses while startValue < target
and then adds startValue - target
after the loop to handle any remaining difference through subtraction operations.
3. Using Inefficient Operations
Using modulo operator (%
) and integer division (//
) instead of bitwise operations can impact performance in competitive programming scenarios.
Less efficient:
if target % 2 == 1: # Modulo operation target += 1 else: target = target // 2 # Integer division
More efficient:
if target & 1: # Bitwise AND target += 1 else: target >>= 1 # Right shift
4. Overthinking the Greedy Choice
Some may question why dividing by 2 (when working backward) is always optimal for even numbers. They might try to implement complex logic to decide between operations.
Why the greedy approach works: When working backward:
- Dividing by 2 reduces the number faster than adding 1
- If we need to eventually reach a smaller
startValue
, reducingtarget
quickly minimizes total operations - The mathematical proof: any sequence of operations can be rearranged to group all multiplications before subtractions without increasing the operation count
Which type of traversal does breadth first search do?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!