There are some robots and factories on the X-axis. You are given an integer array robot
where robot[i]
is the position of the ith
robot. You are also given a 2D integer array factory
where factory[j] = [positionj, limitj]
indicates that positionj
is the position of the jth
factory and that the jth
factory can repair at most limitj
robots.
The positions of each robot are unique. The positions of each factory are also unique. Note that a robot can be in the same position as a factory initially.
All the robots are initially broken; they keep moving in one direction. The direction could be the negative or the positive direction of the X-axis. When a robot reaches a factory that did not reach its limit, the factory repairs the robot, and it stops moving.
At any moment, you can set the initial direction of moving for some robot. Your target is to minimize the total distance traveled by all the robots.
Return the minimum total distance traveled by all the robots. The test cases are generated such that all the robots can be repaired.
Note that
- All robots move at the same speed.
- If two robots move in the same direction, they will never collide.
- If two robots move in opposite directions and they meet at some point, they do not collide. They cross each other.
- If a robot passes by a factory that reached its limits, it crosses it as if it does not exist.
- If the robot moved from a position
x
to a positiony
, the distance it moved is|y - x|
.
Example 1:
Input: robot = [0,4,6], factory = [[2,2],[6,2]] Output: 4 Explanation: As shown in the figure: - The first robot at position 0 moves in the positive direction. It will be repaired at the first factory. - The second robot at position 4 moves in the negative direction. It will be repaired at the first factory. - The third robot at position 6 will be repaired at the second factory. It does not need to move. The limit of the first factory is 2, and it fixed 2 robots. The limit of the second factory is 2, and it fixed 1 robot. The total distance is |2 - 0| + |2 - 4| + |6 - 6| = 4. It can be shown that we cannot achieve a better total distance than 4.
Example 2:
Input: robot = [1,-1], factory = [[-2,1],[2,1]] Output: 2 Explanation: As shown in the figure: - The first robot at position 1 moves in the positive direction. It will be repaired at the second factory. - The second robot at position -1 moves in the negative direction. It will be repaired at the first factory. The limit of the first factory is 1, and it fixed 1 robot. The limit of the second factory is 1, and it fixed 1 robot. The total distance is |2 - 1| + |(-2) - (-1)| = 2. It can be shown that we cannot achieve a better total distance than 2.
Constraints:
1 <= robot.length, factory.length <= 100
factory[j].length == 2
-109 <= robot[i], positionj <= 109
0 <= limitj <= robot.length
- The input will be generated such that it is always possible to repair every robot.
Approach 01:
-
C++
-
Python
class Solution { public: long long minimumTotalDistance(vector<int>& robots, vector<vector<int>>& factories) { ranges::sort(robots); ranges::sort(factories); vector<vector<vector<long>>> memo( robots.size(), vector<vector<long>>(factories.size(), vector<long>(robots.size())) ); return calculateMinDistance(robots, factories, 0, 0, 0, memo); } private: // Calculates the minimum distance to repair robots[i..n) with factories[j..n), // where factories[j] has already repaired `repairedCount` robots. long calculateMinDistance(const vector<int>& robots, const vector<vector<int>>& factories, int robotIndex, int factoryIndex, int repairedCount, vector<vector<vector<long>>>& memo) { if (robotIndex == robots.size()) return 0; if (factoryIndex == factories.size()) return LONG_MAX; if (memo[robotIndex][factoryIndex][repairedCount] > 0) return memo[robotIndex][factoryIndex][repairedCount]; // Option to skip the current factory and try the next one const long skipCurrentFactory = calculateMinDistance(robots, factories, robotIndex, factoryIndex + 1, 0, memo); // Get the factory position and repair limit const int factoryPosition = factories[factoryIndex][0]; const int repairLimit = factories[factoryIndex][1]; // Option to use the current factory if the repair limit allows const long useCurrentFactory = repairLimit > repairedCount ? calculateMinDistance(robots, factories, robotIndex + 1, factoryIndex, repairedCount + 1, memo) + abs(robots[robotIndex] - factoryPosition) : LONG_MAX / 2; // Memoize and return the minimum distance for this configuration return memo[robotIndex][factoryIndex][repairedCount] = min(skipCurrentFactory, useCurrentFactory); } };
class Solution: def minimumTotalDistance(self, robots, factories): robots.sort() factories.sort() memo = [[[None] * len(robots) for _ in range(len(factories))] for _ in range(len(robots))] return self.calculateMinDistance(robots, factories, 0, 0, 0, memo) # Helper function to calculate minimum distance def calculateMinDistance(self, robots, factories, robotIndex, factoryIndex, repairedCount, memo): if robotIndex == len(robots): return 0 if factoryIndex == len(factories): return float('inf') if memo[robotIndex][factoryIndex][repairedCount] is not None: return memo[robotIndex][factoryIndex][repairedCount] # Option to skip the current factory and try the next one skipCurrentFactory = self.calculateMinDistance(robots, factories, robotIndex, factoryIndex + 1, 0, memo) # Get the factory position and repair limit factoryPosition = factories[factoryIndex][0] repairLimit = factories[factoryIndex][1] # Option to use the current factory if the repair limit allows useCurrentFactory = ( self.calculateMinDistance(robots, factories, robotIndex + 1, factoryIndex, repairedCount + 1, memo) + abs(robots[robotIndex] - factoryPosition) if repairLimit > repairedCount else float('inf') ) # Memoize and return the minimum distance for this configuration memo[robotIndex][factoryIndex][repairedCount] = min(skipCurrentFactory, useCurrentFactory) return memo[robotIndex][factoryIndex][repairedCount]
Time Complexity
- Sorting the Robots and Factories:
Sorting both the
robots
andfactories
vectors takes \(O(R \log R + F \log F)\), where \(R\) is the number of robots and \(F\) is the number of factories. - Recursive Calculation with Memoization:
The
calculateMinDistance
function explores different configurations of robot-factory pairs. With three variables (robotIndex
,factoryIndex
, andrepairedCount
), the number of unique states is \(O(R \times F \times R)\), where \(R\) is the number of robots and \(F\) is the number of factories. - Memoization Access:
Each recursive state is computed once and then memoized, making each lookup \(O(1)\). Therefore, the overall complexity of the recursive calculations is \(O(R \times F \times R)\).
- Overall Time Complexity:
The total time complexity is \(O(R \log R + F \log F + R \times F \times R)\).
Space Complexity
- Memoization Storage:
The memoization table
memo
requires \(O(R \times F \times R)\) space, as it stores results for each unique state. - Auxiliary Stack Space:
The recursion depth could go up to \(O(R + F)\) in the worst case, requiring \(O(R + F)\) stack space.
- Overall Space Complexity:
The overall space complexity is \(O(R \times F \times R)\), dominated by the memoization storage.