SPOJ Medium

Aggressive Cows

Aggressive Cows is a classic GFG problem, also on SPOJ as AGGRCOW; it is not on LeetCode. You are given the positions of n stalls on a straight line (distinct, in no particular order) and a number of cows k.

Place all k cows in different stalls so that the smallest distance between any two cows is as large as possible, and return that distance. In short: maximize the minimum distance.

Constraints
  • 2 <= n <= 10⁵
  • 2 <= k <= n
  • 0 <= stalls[i] <= 10⁹
binary-searchgreedy
Open on SPOJ ↗
02

Intuition

Trying every way to place the cows is far too slow. Flip the question around: instead of finding the best distance, check a distance. Asking "can k cows be placed at least d apart?" is easy, because placing each cow as early as possible is always safe.

The check is also monotone: if d works, every smaller distance works; if it fails, every larger one fails. So the answers read yes yes yes ... no no no, and a pattern that flips once is exactly what binary search on the answer finds. To maximize the minimum distance, search for the last yes.

How to spot this pattern

"Maximise the minimum" (or "minimise the maximum") plus a quick way to check one candidate answer. That is binary search on the answer: the search space is the answer's range, not the input array. The same template solves Koko Eating Bananas, Capacity to Ship Packages and Allocate Minimum Pages.

03

Approach

Try it first

Before reading on, take stalls [1, 2, 4, 8, 9] and k = 3. Can every gap be at least 4? At least 3? What does the yes/no pattern look like as the distance grows?

1

Sort the stalls

Sort stalls first, at O(n log n). Cows are placed by walking left to right, and the gap between neighbouring cows only means something when the stalls are in order; the input usually is not.

2

Write the greedy check

fits(d): put a cow in stalls[0]; walk right and place the next cow in the first stall with s - last >= d. Return count >= k. Taking the earliest possible stall leaves the most room for the cows that follow, so if greedy fails, no placement succeeds.

3

Binary search the distance

  • Set lo = 1 and hi = stalls[-1] - stalls[0], the smallest and largest gaps possible.
  • Take mid = (lo + hi) // 2.
  • If fits(mid), record best = mid and try larger with lo = mid + 1.
  • Otherwise try smaller with hi = mid - 1.

Recording best on every success means the loop never has to land exactly on the answer.

4

Return the last distance that fit

When lo > hi, best holds the largest distance that passed the check. Every value above it failed, and because the check is monotone, no larger distance can work. The aggressive cows solution costs the sort plus O(n log(range)) for the search.

04

Aggressive Cows solution in Python | C++ | Java

▶1class Solution:
▶2 def aggressiveCows(self, stalls: List[int], k: int) -> int:
▶3 stalls.sort()
▶4 
▶5 def fits(d: int) -> bool:
▶6 count, last = 1, stalls[0]
▶7 for s in stalls[1:]:
▶8 if s - last >= d:
▶9 count, last = count + 1, s
▶10 return count >= k
▶11 
▶12 lo, hi, best = 1, stalls[-1] - stalls[0], 0
▶13 while lo <= hi:
▶14 mid = (lo + hi) // 2
▶15 if fits(mid):
▶16 best, lo = mid, mid + 1
▶17 else:
▶18 hi = mid - 1
▶19 return best
12489d range12345678place k = 3 cows
k3cows to place
lo, hi1, 8smallest and largest possible minimum gap
Search the answer, not the array. The best minimum gap is somewhere between 1 and 8 (the whole line). And it is monotone: if every gap can be at least d, it can also be at least any smaller d. So binary search over d, and for each d just ask: can 3 cows fit?
7C1124C289d range12345678try d = 4
d4middle of [1, 8]
cows placed2greedy, left to right
Place cows greedily: the first cow goes in stall 1, and each next cow goes in the first stall at least 4 past the last cow. Taking the earliest possible stall always leaves the most room for the rest, so if greedy cannot fit 3 cows, nothing can. Placed: 1, 8.
7C1124C289d range12345678d = 4 fails → try smaller
fitsno2 of 3 cows
best0largest d that worked
lo, hi1, 3
4 fails: only 2 cows fit. Every larger gap fails too, so look only at smaller gaps: hi = 3.
34C112C24C389d range12345678try d = 2
d2middle of [1, 3]
cows placed3greedy, left to right
Place cows greedily: the first cow goes in stall 1, and each next cow goes in the first stall at least 2 past the last cow. Taking the earliest possible stall always leaves the most room for the rest, so if greedy cannot fit 3 cows, nothing can. Placed: 1, 4, 8.
34C112C24C389d range12345678d = 2 fits → try bigger
fitsyes3 of 3 cows
best2largest d that worked
lo, hi3, 3
2 works, so every gap up to 2 works too. Record it and look only at larger gaps: lo = 3.
34C112C24C389d range12345678try d = 3
d3middle of [3, 3]
cows placed3greedy, left to right
Place cows greedily: the first cow goes in stall 1, and each next cow goes in the first stall at least 3 past the last cow. Taking the earliest possible stall always leaves the most room for the rest, so if greedy cannot fit 3 cows, nothing can. Placed: 1, 4, 8.
34C112C24C389d range12345678d = 3 fits → try bigger
fitsyes3 of 3 cows
best3largest d that worked
lo, hi4, 3
3 works, so every gap up to 3 works too. Record it and look only at larger gaps: lo = 4.
34C112C24C389d range12345678return 3
answer3lo passed hi
Answer 3. The search stopped when lo passed hi; 3 is the last distance that fit all 3 cows. Each check is one O(n) pass, and there are about log(range) checks.
05

Common pitfalls

Forgetting to sort the stalls

✗ Wrong
def aggressiveCows(self, stalls, k):
    lo, hi = 1, max(stalls) - min(stalls)
✓ Right
def aggressiveCows(self, stalls, k):
    stalls.sort()
    lo, hi = 1, stalls[-1] - stalls[0]

The greedy check walks left to right and compares each stall with the previous cow. On unsorted input like [10, 1, 2, 7, 5] it measures meaningless distances and returns a wrong answer.

Returning lo or mid instead of the last success

✗ Wrong
while lo <= hi:
    mid = (lo + hi) // 2
    if fits(mid):
        lo = mid + 1
    else:
        hi = mid - 1
return lo
✓ Right
if fits(mid):
    best, lo = mid, mid + 1
...
return best  # equivalently hi

When the loop ends, lo is one past the answer. Keep best (or return hi) so the value returned is one that actually passed the check.

Requiring exactly k cows in the check

✗ Wrong
return count == k
✓ Right
return count >= k

Greedy keeps placing cows as long as stalls allow. For [1, 2, 4, 8, 9], k = 3 and d = 1 it places 5 cows; with == k that small, obviously feasible distance is rejected and the search heads the wrong way.

06

Edge cases

k = 2

The two cows go in the end stalls, so the answer is max - min. That is also why the search's upper bound starts there.

k equals the number of stalls

Every stall gets a cow, so the answer is the smallest gap between neighbouring stalls.

Large coordinates

Positions up to 10^9 need only about 30 checks. C++ and Java compute lo + (hi - lo) / 2 so mid cannot overflow.

07

Complexity

Time
O(n log n + n log R)
Space
O(1)
Sorting is O(n log n). R = max - min is the answer range; each of the O(log R) binary-search steps runs an O(n) greedy check. Sorting in place needs no extra space beyond the language's sort.
08

Binary search on the answer: same template, different check

Each of these searches a range of possible answers and checks one candidate with a greedy pass.

ProblemSearch overCheck for a candidateKeep
Aggressive Cowsminimum distance dgreedy places >= k cows at gap dlargest that fits
Koko Eating Bananas (875)eating speedtotal hours <= hsmallest that fits
Capacity to Ship Packages (1011)ship capacitydays needed <= Dsmallest that fits
Allocate Minimum Pagesmax pages per studentstudents needed <= msmallest that fits
09

Aggressive Cows FAQ

What is the aggressive cows problem?

Given stall positions on a line and k cows, place the cows in stalls so that the smallest distance between any two cows is as large as possible, and return that distance. For stalls [1, 2, 4, 8, 9] and k = 3 the answer is 3 (cows at 1, 4 and 8).

What are the steps to solve aggressive cows?
  • Sort the stalls.
  • Check(d): place a cow at the first stall, then at each first stall at least d from the last cow; succeed if k cows fit.
  • Monotone: if d fits, every smaller d fits.
  • Binary search d in [1, max - min]: on success record d and go right, on failure go left.
  • Answer: the largest d that fit.
  • Complexity: O(n log n + n log R) time, O(1) extra space.
  • Example: [1, 2, 4, 8, 9], k = 3 gives 3.
Why does binary search work for aggressive cows?

Feasibility is monotone in d. If cows can be placed with every gap at least d, the same placement also has every gap at least d - 1. So the set of feasible distances is a prefix 1..answer, and binary search finds its end in O(log R) checks.

Why is the greedy placement correct?

Putting each cow in the earliest stall that respects the gap leaves the most space to its right for the remaining cows. Any valid placement can be shifted left to match greedy without breaking a gap, so if greedy fits fewer than k cows, no placement fits k.

Is aggressive cows on LeetCode?

Not under that name; it comes from SPOJ (AGGRCOW) and GeeksforGeeks. LeetCode's Magnetic Force Between Two Balls (1552) is the same problem with balls and baskets.