Python 2998.py
class Solution:
def minimumOperationsToMakeEqual(self, x: int, y: int) -> int:
# edge case
if x <= y:
return y - x # always +1
have = set()
todo = [ x ]
steps = 0
while todo:
next = set()
for t in todo:
if t == y:
return steps
have.add(t)
# helper function: store only new values
def add(x):
if x not in have:
next.add(x)
if t < x + 11: # reach the next multiple of 11
add(t + 1)
if t > y:
add(t - 1)
if t % 11 == 0:
add(t // 11)
if t % 5 == 0:
add(t // 5)
todo = next
steps += 1
Difficulty
and has an acceptance rate of about 49% (60k+ submissions).
Typical patterns to solve this problem:
- Breadth First Search
- Dynamic Programming
- Memoization
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The minimumOperationsToMakeEqual method computes the smallest number of allowed operations required to transform an integer x into another integer y. The permitted operations are:
- increase the current value by 1
- decrease the current value by 1 (only when the current value is larger than
y) - divide the current value by 5 when it is a multiple of 5
- divide the current value by 11 when it is a multiple of 11
The algorithm employs a breadth-first search (BFS) over the state space of integer values, expanding outward from the start value x until the target y is reached.
Key Steps
The function begins with a simple edge-case check that handles the situation where the start value is not larger than the target:
if x <= y:
return y - x # always +1
When x exceeds y, the BFS infrastructure is set up. A set named have records all values that have already been visited, while a list todo holds the current frontier of values to explore. The variable steps counts how many layers of the BFS have been processed:
have = set()
todo = [x]
steps = 0
The outer while loop continues as long as there are values left to explore. At the start of each iteration a fresh set next is created to collect the successors of the current frontier:
while todo:
next = set()
Inside the loop, each value t from the current frontier is examined. If t equals the target y, the algorithm terminates immediately, returning the number of steps taken so far:
for t in todo:
if t == y:
return steps
The current value is added to the visited set, guaranteeing that it will not be processed again:
have.add(t)
A small helper function add is defined locally to encapsulate the logic of inserting a new candidate into the next set only when it has not been seen before:
def add(x):
if x not in have:
next.add(x)
The algorithm then generates possible successors based on the allowed operations:
- Increment by 1 is considered only while the current value is less than
x + 11. This bound prevents the search from drifting arbitrarily far above the start value:
if t < x + 11: # reach the next multiple of 11
add(t + 1)
- Decrement by 1 is always allowed when the current value is still above the target:
if t > y:
add(t - 1)
- Division by 11 is offered when the current value is exactly divisible by 11:
if t % 11 == 0:
add(t // 11)
- Division by 5 is offered when the current value is exactly divisible by 5:
if t % 5 == 0:
add(t // 5)
After all successors for the current frontier have been collected, the todo list is replaced with the newly built next set, and the step counter is incremented, moving the BFS to the next layer:
todo = next
steps += 1
The loop repeats until the target is found, at which point the function returns the minimal number of operations.
Edge Cases Handled
The early return for x <= y directly covers the scenario where only increments are needed, guaranteeing the correct answer without entering the BFS. The condition if t == y inside the BFS loop ensures that the algorithm stops as soon as the target is reached, even if it appears in the middle of a frontier expansion. The add helper prevents re-adding values that have already been visited, thereby avoiding infinite loops caused by cycles such as repeatedly adding and subtracting 1. Division operations are guarded by modulus checks (t % 11 == 0 and t % 5 == 0), ensuring that only integer results are produced. The increment bound t < x + 11 limits the upward exploration to a narrow window, preventing the search from exploring values that could never lead to a shorter path to y.
Performance Optimizations
The use of a set for the have collection provides constant-time membership tests, making the duplicate-avoidance check in add highly efficient. Storing the next frontier in a set (next = set()) automatically eliminates duplicate successors that could arise from different parent nodes, reducing the number of states that need to be processed in subsequent iterations. The early exit when the target is found eliminates unnecessary exploration of deeper BFS layers. By restricting the increment operation to values less than x + 11, the algorithm curtails the growth of the search space, focusing effort on the region most likely to contain the optimal path. The local helper add centralizes the duplicate-check logic, avoiding repeated code and ensuring that each candidate is vetted consistently before insertion.
ELI5 - Simplified Explanation
Imagine playing a game where the goal is to turn a big starting number into a smaller target number. You start with a pile of numbers to work on. Since you want to reach the goal as quickly as possible, you check all the numbers you can reach in one move. You can either make your number a little bit bigger, a little bit smaller, or divide it if it fits perfectly into groups of five or eleven.
Every time you finish checking your current numbers, you count that as one big step. You keep adding new numbers you discover to your list, but you make sure to ignore any numbers you have already looked at so you do not run in circles. You keep repeating this process, gathering all the new possibilities for the next turn, until you finally land on the target number. Once you find it, you know exactly how many steps it took to get there.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | |||||||
|---|---|---|---|---|---|---|---|
| author | solutions | ||||||
| www | AlgoMonster | C++ | Java | Python | TS | ||
| WalkCCC | C++ | Java | Python | ||||
| leetcode.ca | C++ | Java | Go | Python | TS | ||
| Progiez | C++ | Java | Python | ||||
| github | charles-wangkai | Java | |||||
| fsq | C++ | ||||||
| JoshCrozier | JS | ||||||
| wisdompeak | C++ | ||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||
| WalkCCC | C++, Python, Java | ||||||
| leetcode.ca | C++, Go, Java, Python, TypeScript | ||||||
| Progiez | C++, Java, Python | ||||||
| charles-wangkai | Java | ||||||
| fsq | C++ | ||||||
| JoshCrozier | JavaScript | ||||||
| wisdompeak | C++ |
Videos
Similar Problems
- problem 2850: Minimum Moves to Spread Stones Over Grid
Solved On The Same Day
- problem 3038: Maximum Number of Operations With the Same Score I
- problem 3002: Maximum Size of a Set After Removals
- problem 3030: Find the Grid of Region Average
- problem 3035: Maximum Palindromes After Operations
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality