LeetCode 1956 - Minimum Time For K Virus Variants to Spread
The problem describes multiple virus variants spreading across an infinite 2D grid. Each virus starts from its own origin point on day 0.
Difficulty: 🔴 Hard
Topics: Array, Math, Binary Search, Geometry, Enumeration
Solution
Problem Understanding
The problem describes multiple virus variants spreading across an infinite 2D grid. Each virus starts from its own origin point on day 0. Every day, a virus spreads one step in the four cardinal directions, which means after d days, a virus can reach every point whose Manhattan distance from its origin is at most d.
The Manhattan distance between two points (x1, y1) and (x2, y2) is:
$$|x1 - x2| + |y1 - y2|$$
A point on the grid contains a virus variant on day d if the virus can reach that point within d steps.
We are given:
points[i] = [xi, yi], the starting location of thei-thvirus- An integer
k
We must determine the minimum number of days such that there exists at least one grid cell that contains at least k different virus variants simultaneously.
An important detail is that multiple virus variants may originate from the same coordinates. Even though the coordinates may repeat, the virus variants themselves are considered distinct.
The constraints are relatively small:
n <= 50- Coordinates are at most
100
This tells us that an exponential or heavy geometric approach might still be acceptable if carefully optimized. Since the answer represents a minimum feasible day count, binary search becomes a strong candidate.
Several edge cases are important:
- Multiple viruses may already overlap at day
0 kmay equaln, meaning we need a point reachable by all variants- Viruses may be extremely far apart
- There may be many candidate intersection regions
- The optimal meeting point does not necessarily coincide with an original virus location
The infinite grid sounds intimidating, but the geometry of Manhattan distance gives the problem strong structure that we can exploit.
Approaches
Brute Force Approach
A direct brute-force solution would simulate day-by-day virus spreading and track which viruses reach each cell.
For a fixed number of days d, each virus covers a diamond-shaped region:
$$|x - xi| + |y - yi| \le d$$
One brute-force strategy would enumerate all grid cells inside a sufficiently large bounding box and count how many virus regions contain each point.
This works because if any point is covered by at least k viruses, then d is feasible.
However, this approach becomes inefficient because:
- The grid is infinite
- Coverage regions grow quadratically with distance
- The bounding box for large distances becomes huge
- Repeating this for binary search would be extremely expensive
Even with coordinate limits near 100, the answer can exceed that range significantly.
Key Insight
The critical observation is that Manhattan distance constraints can be transformed into linear inequalities.
A point (x, y) satisfies:
$$|x - xi| + |y - yi| \le d$$
if and only if the following are all true:
$$x + y \le xi + yi + d$$
$$x + y \ge xi + yi - d$$
$$x - y \le xi - yi + d$$
$$x - y \ge xi - yi - d$$
Each virus therefore defines a rectangle in transformed coordinates:
u = x + yv = x - y
For a fixed d, we need to determine whether there exists a point covered by at least k such rectangles.
Since n <= 50, we can enumerate subsets indirectly by sweeping candidate boundaries.
We binary search the answer:
- If a day count
dis feasible, larger values are also feasible - This monotonicity makes binary search valid
For each candidate d, we test feasibility geometrically.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(R² × n × log R) | O(R²) | Enumerates grid cells in a large bounding box |
| Optimal | O(log M × n³) | O(n²) | Binary search with geometric intersection checking |
Here, M is the maximum possible answer range.
Algorithm Walkthrough
Step 1, Binary Search the Answer
The answer is monotonic.
If it is possible for k viruses to overlap after d days, then it is also possible after d + 1 days because every virus region only expands.
This allows binary search over the minimum feasible day.
We initialize:
left = 0right = 200
A larger upper bound such as 400 or 1000 also works safely.
Step 2, Convert Each Virus Region
For a fixed day d, each virus covers:
$$|x - xi| + |y - yi| \le d$$
Transform coordinates:
$$u = x + y$$
$$v = x - y$$
Then the virus coverage becomes:
$$xi + yi - d \le u \le xi + yi + d$$
$$xi - yi - d \le v \le xi - yi + d$$
This is an axis-aligned rectangle in (u, v) space.
Step 3, Enumerate Candidate Intersections
We need to know whether at least k rectangles overlap.
A standard geometric trick is that any overlap region boundary must come from rectangle boundaries.
We collect all candidate u coordinates from:
- lower bounds
- upper bounds
Similarly for v.
Then we test every candidate (u, v) pair.
For each candidate point:
- Count how many rectangles contain it
- If count reaches
k, then daydis feasible
Step 4, Feasibility Check
For each virus rectangle:
- Check whether:
$$u_{min} \le u \le u_{max}$$
and
$$v_{min} \le v \le v_{max}$$
If true, the rectangle contains the point.
If at least k rectangles contain the point, return True.
Step 5, Finish Binary Search
If day mid is feasible:
- Store answer
- Search smaller values
Otherwise:
- Search larger values
Eventually binary search converges to the minimum feasible day.
Why it works
The transformed geometry converts diamond-shaped Manhattan regions into axis-aligned rectangles. Any overlap region among rectangles must have boundaries derived from rectangle edges. Therefore, if an overlap of size at least k exists, there exists a candidate boundary point we will enumerate.
Binary search is valid because virus regions only grow over time, making feasibility monotonic.
Python Solution
from typing import List
class Solution:
def minDayskVariants(self, points: List[List[int]], k: int) -> int:
n = len(points)
def can(days: int) -> bool:
rectangles = []
u_values = set()
v_values = set()
for x, y in points:
u_min = x + y - days
u_max = x + y + days
v_min = x - y - days
v_max = x - y + days
rectangles.append((u_min, u_max, v_min, v_max))
u_values.add(u_min)
u_values.add(u_max)
v_values.add(v_min)
v_values.add(v_max)
for u in u_values:
for v in v_values:
count = 0
for u_min, u_max, v_min, v_max in rectangles:
if u_min <= u <= u_max and v_min <= v <= v_max:
count += 1
if count >= k:
return True
return False
left = 0
right = 400
while left < right:
mid = (left + right) // 2
if can(mid):
right = mid
else:
left = mid + 1
return left
The implementation starts by defining a helper function can(days) that determines whether at least k viruses can overlap after a given number of days.
Inside this helper, every virus region is converted into a rectangle in transformed (u, v) coordinates. We store:
- minimum and maximum
u - minimum and maximum
v
We also collect all rectangle boundaries into candidate coordinate sets.
Next, we enumerate every candidate (u, v) pair. For each pair, we count how many rectangles contain the point. If the count reaches k, the configuration is feasible.
The outer binary search repeatedly calls this feasibility function. Since feasibility is monotonic, binary search correctly finds the smallest valid number of days.
Go Solution
func minDayskVariants(points [][]int, k int) int {
can := func(days int) bool {
type Rect struct {
uMin int
uMax int
vMin int
vMax int
}
rectangles := []Rect{}
uSet := map[int]bool{}
vSet := map[int]bool{}
for _, p := range points {
x := p[0]
y := p[1]
uMin := x + y - days
uMax := x + y + days
vMin := x - y - days
vMax := x - y + days
rectangles = append(rectangles, Rect{
uMin: uMin,
uMax: uMax,
vMin: vMin,
vMax: vMax,
})
uSet[uMin] = true
uSet[uMax] = true
vSet[vMin] = true
vSet[vMax] = true
}
uValues := []int{}
for val := range uSet {
uValues = append(uValues, val)
}
vValues := []int{}
for val := range vSet {
vValues = append(vValues, val)
}
for _, u := range uValues {
for _, v := range vValues {
count := 0
for _, rect := range rectangles {
if rect.uMin <= u && u <= rect.uMax &&
rect.vMin <= v && v <= rect.vMax {
count++
}
}
if count >= k {
return true
}
}
}
return false
}
left := 0
right := 400
for left < right {
mid := (left + right) / 2
if can(mid) {
right = mid
} else {
left = mid + 1
}
}
return left
}
The Go implementation mirrors the Python approach closely.
Since Go does not have tuples, we define a Rect struct to store rectangle boundaries. Hash sets are implemented using map[int]bool.
Slices are used for candidate coordinate enumeration. Integer overflow is not a concern because coordinates and distances remain small.
Worked Examples
Example 1
points = [[1,1],[6,1]]
k = 2
Day = 2
Virus 1:
$$|x-1| + |y-1| \le 2$$
Virus 2:
$$|x-6| + |y-1| \le 2$$
The regions do not overlap.
Day = 3
Virus 1 reaches:
$$x \in [ -2, 4 ]$$
Virus 2 reaches:
$$x \in [ 3, 9 ]$$
Overlap exists at:
(3,1)(4,1)
So answer is 3.
Example 2
points = [[3,3],[1,2],[9,2]]
k = 2
Day = 2
First virus rectangle:
| Value | Range |
|---|---|
| u | [4, 8] |
| v | [-2, 2] |
Second virus rectangle:
| Value | Range |
|---|---|
| u | [1, 5] |
| v | [-3, 1] |
The rectangles overlap.
A valid point is (2,2).
So answer is 2.
Example 3
points = [[3,3],[1,2],[9,2]]
k = 3
We need all three viruses to overlap.
At day 3, no common intersection exists.
At day 4, point (5,2) works:
| Virus | Distance |
|---|---|
| (3,3) | 3 |
| (1,2) | 4 |
| (9,2) | 4 |
All are within reach.
So answer is 4.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(log M × n³) | Binary search times rectangle overlap checking |
| Space | O(n²) | Stores rectangles and candidate boundaries |
For each binary search iteration:
- We create
nrectangles - We enumerate at most
2ncandidateuvalues - We enumerate at most
2ncandidatevvalues - For every candidate pair, we scan all
nrectangles
This gives:
$$O((2n)(2n)n) = O(n^3)$$
Binary search adds a multiplicative log M factor.
Since n <= 50, this is easily fast enough.
Test Cases
sol = Solution()
assert sol.minDayskVariants([[1,1],[6,1]], 2) == 3
# Basic two-virus overlap
assert sol.minDayskVariants([[3,3],[1,2],[9,2]], 2) == 2
# Need overlap among any two viruses
assert sol.minDayskVariants([[3,3],[1,2],[9,2]], 3) == 4
# Need overlap among all viruses
assert sol.minDayskVariants([[1,1],[1,1]], 2) == 0
# Multiple variants already at same point
assert sol.minDayskVariants([[1,1],[100,100]], 2) == 99
# Very distant viruses
assert sol.minDayskVariants([[1,1],[2,2],[3,3]], 2) == 1
# Small overlap quickly achievable
assert sol.minDayskVariants([[1,1],[2,2],[3,3]], 3) == 2
# All viruses need common meeting point
assert sol.minDayskVariants([[1,1],[1,100],[100,1],[100,100]], 4) == 99
# Large symmetric configuration
assert sol.minDayskVariants([[5,5],[5,5],[5,5]], 3) == 0
# All variants already overlap
| Test | Why |
|---|---|
[[1,1],[6,1]], k=2 |
Basic example |
[[3,3],[1,2],[9,2]], k=2 |
Partial overlap |
[[3,3],[1,2],[9,2]], k=3 |
Full overlap |
| Duplicate coordinates | Tests day 0 answer |
| Very distant points | Tests large answer |
| Small clustered points | Tests quick overlap |
k = n |
Tests all-virus requirement |
| Symmetric corners | Tests geometry correctness |
| All same coordinate | Tests immediate overlap |
Edge Cases
Multiple Viruses Starting at the Same Point
Different virus variants may originate from identical coordinates. A buggy implementation might accidentally deduplicate them and lose variants.
This implementation never removes duplicates. Each virus produces its own rectangle, so if multiple variants start together, the overlap count immediately reflects that. As a result, the answer can correctly be 0.
Requiring All Viruses to Overlap
When k = n, every virus must simultaneously cover some common point. This is the strictest possible requirement.
The rectangle intersection logic naturally handles this because we simply count how many rectangles contain a candidate point. The algorithm does not distinguish between partial and complete overlap.
Optimal Point Not Equal to Any Virus Origin
A naive implementation might only check original virus coordinates. That fails because the optimal overlap point may lie somewhere entirely different.
The transformed-coordinate rectangle approach correctly searches all possible intersection regions indirectly through boundary enumeration, ensuring no valid overlap point is missed.