Python Algorithms & Data Structures track

Lesson 12 · Part 3: Sorting & Searching

Parametric Search

Parametric search and the 'maximise the feasible value' pattern

Part 3: Sorting & Searching4 min readFree lesson

Part 03 – Sorting and Searching | Difficulty: ⭐⭐⭐☆☆


Introduction: Applying Binary Search to the 'Answer'

Parametric Search is a technique that applies binary search not to values but to the space of possible answers.

Core Pattern

Determine with an O(n) decision function whether "a certain value X is possible," and use binary search over the range [lo, hi] of X to find the optimal X in O(n log(range)).

Common problem types:

Pattern Example
Maximizing the Minimum Maximizing the minimum distance when installing K routers
Minimizing the Maximum Minimizing the maximum burden of one person when distributing among K people
Possible Maximum The maximum H that yields at least M meters when cutting trees

Learning Goals

  1. Define a decision function (feasibility function) and check its monotonicity.
  2. Memorize the pattern templates for "Maximizing the Minimum" / "Minimizing the Maximum."
  3. Set the search direction (updating left vs right) correctly according to the problem type.

Key Concepts

Parametric Search Template

def feasible(mid: int) -> bool:
    #Does the mid value satisfy the condition?
    ...

left, right = minimum value, maximum value
answer = left     #Or right (initial value determined depending on the problem)

while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        answer = mid           #Update the possible value
        left = mid + 1         #Try a larger value (maximize)
        #Or right = mid - 1 (minimize)
    else:
        right = mid - 1        #Need to decrease
        #Or left = mid + 1 (need to increase)

Monotonicity of the Decision Function

Parametric search is possible only if the decision function is monotonic:

Maximization problem (possible maximum value):
X=1: ✅  X=2: ✅  X=3: ✅  X=4: ❌  X=5: ❌
→ After a certain point, all ❌ → Binary search possible

Minimization problem (possible minimum value):
X=1: ❌  X=2: ❌  X=3: ✅  X=4: ✅  X=5: ✅
→ After a certain point, all ✅ → Binary search possible

Pattern for Minimizing the Maximum

"When dividing into K groups, minimize the size of the largest group"

def feasible(max_size):
    #Is it possible to divide into K groups or fewer due to max_size limit?
    groups = 1
    current = 0
    for item in items:
        if current + item > max_size:
            groups += 1
            current = item
        else:
            current += item
    return groups <= K

left, right = max(items), sum(items)
answer = right
while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        answer = mid      #Update minimum value
        right = mid - 1   #Try a smaller value
    else:
        left = mid + 1

Maximum of the minimum pattern

"When placing K items, maximize the minimum distance between adjacent items"

def feasible(min_dist):
    #Is it possible to place K items with at least min_dist distance?
    ...

left, right = 1, max_position
answer = 0
while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        answer = mid      #Update the maximum value
        left = mid + 1    #Try a larger value
    else:
        right = mid - 1

Example

File Contents
src/01_parametric_template.py General parameter search template
src/02_cable_cutting.py Cable/Wood cutting variant
src/03_book_pages.py Distributing book pages to K people (minimize the maximum)
src/04_kth_smallest.py K-th smallest number in multiplication table

5 Common Mistakes

  1. Setting the decision function in the wrong direction
  • Maximization: if condition is satisfied left = mid + 1 (search for larger values)
  • Minimization: if condition is satisfied right = mid - 1 (search for smaller values)
  • Doing it the opposite way can cause infinite loops or wrong answers
  1. Errors in setting search range (lo, hi)
  • If lo is too high or hi is too low, you can miss the answer
  • Safely: lo = possible minimum, hi = possible maximum
  1. Using a non-monotonic decision function
  • ✅ ❌ ✅ pattern makes binary search impossible
  • Need to reanalyze the problem
  1. Integer/Real-Number boundary handling
  • Integer search: while left <= right
  • Floating-point search: while right - left > eps (e.g., 1e-9)
  1. **Not initializing answer value
  • Handling cases where feasible is always False was omitted
  • Always explicitly set the initial value of answer

Summary

Problem Type Decision Function Direction answer Update
Maximization (possible maximum) Satisfies → left++ answer = mid when feasible
Minimization (possible minimum) Satisfies → right-- answer = mid when feasible

Try It Yourself

  1. Solve the tree cutting problem (Baekjoon 2805) using a decision function.
  2. Explain the monotonicity of the decision function in the router installation problem (Baekjoon 2110).
  3. Calculate the search range for K=1 and K=n in the book distribution problem.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_parametric_template.pyPython
"""Topic: General Template for Parameter Search
Time: O(n log(range))
Space: O(1) (excluding the decision function)"""

from collections.abc import Callable


#── General Template: Maximization ───────────────────────────────────────────────

def parametric_max(
    lo: int,
    hi: int,
    feasible: Callable[[int], bool],
) -> int:
    """Search for the maximum possible value.

    If feasible(mid) = True, assume all values less than or equal to mid are also possible.
    (Monotonically decreasing: ✅✅✅❌❌ pattern)

    Returns:
        The maximum value that satisfies the condition"""
    answer = lo - 1     #Default when nothing is possible
    while lo <= hi:
        mid = (lo + hi) // 2
        if feasible(mid):
            answer = mid
            lo = mid + 1    #Try a larger value
        else:
            hi = mid - 1    #Need to decrease
    return answer


def parametric_min(
    lo: int,
    hi: int,
    feasible: Callable[[int], bool],
) -> int:
    """Search for the minimum possible value.

    If feasible(mid) = True, assume all values greater than or equal to mid are also possible.
    (Monotonically increasing: ❌❌✅✅✅ pattern)

    Returns:
        The minimum value that satisfies the condition"""
    answer = hi + 1     #Default when nothing is possible
    while lo <= hi:
        mid = (lo + hi) // 2
        if feasible(mid):
            answer = mid
            hi = mid - 1    #Try a smaller value
        else:
            lo = mid + 1    #Must press
    return answer


#── Example 1: Maximizing the number of elements in the array with sum at least the target ─────────────

def demo_array_sum() -> None:
    """Find the minimum k such that the sum of the first k elements is at least S."""
    arr = [1, 3, 2, 4, 2, 5, 1, 3]
    S = 10
    prefix = [0]
    for x in arr:
        prefix.append(prefix[-1] + x)

    def feasible_min_k(k: int) -> bool:
        #Is the sum of the first k elements at least S?
        return prefix[k] >= S

    min_k = parametric_min(1, len(arr), feasible_min_k)
    print(f"Array: {arr}, Target sum: {S}")
    print(f"Minimum k with sum at least {S}: {min_k}  (Sum={prefix[min_k]})")


#── Example 2: Monotonicity Visualization ─────────────────────────────────────────────

def visualize_monotone() -> None:
    """Visualize the monotonicity of the decision function."""
    trees = [20, 15, 10, 17]
    need = 7

    def feasible_cut(h: int) -> bool:
        return sum(max(0, t - h) for t in trees) >= need

    print(f"\n=== Monotonicity Visualization ===")
    print(f"Tree heights: {trees}, Needed amount: {need}")
    print(f"{'H':>4}  {'Collected':>6}  {'Possible?':>6}")
    for h in range(0, max(trees) + 1):
        collected = sum(max(0, t - h) for t in trees)
        ok = feasible_cut(h)
        print(f"{h:>4}  {collected:>6}  {'✅' if ok else '❌'}")

    answer = parametric_max(0, max(trees), feasible_cut)
    print(f"\n→ Maximum possible H = {answer}")


#── Example 3: Real Parameter Search ───────────────────────────────────────────────

def parametric_float(
    lo: float,
    hi: float,
    feasible: Callable[[float], bool],
    eps: float = 1e-9,
    max_iter: int = 100,
) -> float:
    """Search over real-number parameters.

    Terminate when the number of iterations reaches max_iter or convergence is within eps."""
    for _ in range(max_iter):
        if hi - lo < eps:
            break
        mid = (lo + hi) / 2
        if feasible(mid):
            lo = mid
        else:
            hi = mid
    return (lo + hi) / 2


def demo_float_search() -> None:
    """Search over real-number parameters: finding sqrt(2)."""
    target = 2.0

    def feasible_sqrt(x: float) -> bool:
        return x * x <= target

    result = parametric_float(0.0, 2.0, feasible_sqrt)
    import math
    print(f"\n=== Real Parameter Search ===")
    print(f"sqrt(2) search result: {result:.10f}")
    print(f"math.sqrt(2)     : {math.sqrt(2):.10f}")
    print(f"Error              : {abs(result - math.sqrt(2)):.2e}")


#── Main ─────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    demo_array_sum()
    visualize_monotone()
    demo_float_search()

    #Simple verification: minimum x in range 0~100 with x*x >= 64
    result = parametric_min(0, 100, lambda x: x * x >= 64)
    assert result == 8, f"Expected 8, got {result}"
    print(f"Verification: Minimum value of x² ≥ 64 = {result}  PASS")
src/02_cable_cutting.pyPython
"""Topic  : Cable/Tree Cutting — Parameter Search
Time   : O(N log(max_length))
Space  : O(N)

Problem: From N cables, how many pieces of length L can be made at most?
         (Or) What is the maximum length L to make at least K pieces?"""


#── Decision Function ─────────────────────────────────────────────────────────

def count_pieces(cables: list[int], length: int) -> int:
    """Number of pieces obtained when cut to length `length`."""
    return sum(c // length for c in cables)


def can_make_k(cables: list[int], length: int, k: int) -> bool:
    """Can we make at least K pieces with length `length`?"""
    return count_pieces(cables, length) >= k


#── Parameter Search ────────────────────────────────────────────────────────────

def max_length_for_k(cables: list[int], k: int) -> int:
    """Maximum length to make at least K pieces.

    Returns:
        Maximum length (0 if impossible)"""
    if not cables:
        return 0
    left, right = 1, max(cables)
    answer = 0

    while left <= right:
        mid = (left + right) // 2
        if can_make_k(cables, mid, k):
            answer = mid
            left = mid + 1     #Try longer length
        else:
            right = mid - 1    #Need to decrease
    return answer


def min_cables_for_k(cables: list[int], k: int) -> int:
    """Maximum of the minimum number of pieces for each piece when making exactly k pieces.
    (= Same problem as `max_length_for_k`)"""
    return max_length_for_k(cables, k)


#── Step Visualization ─────────────────────────────────────────────────────────

def visualize_cutting(cables: list[int], k: int) -> None:
    """Visualization of the search process."""
    print(f"Cable lengths: {cables}")
    print(f"Target number K: {k}")
    print(f"{'L':>5}  {'Number of pieces obtained':>12}  {'Possible?':>6}")
    max_len = max(cables)
    for l in range(1, min(max_len + 1, 30)):   #Output only if 30 or less
        pieces = count_pieces(cables, l)
        ok = pieces >= k
        bar = "█" * min(pieces, 30)
        print(f"{l:>5}  {pieces:>12}  {'✅' if ok else '❌'}  {bar}")
    if max_len >= 30:
        print(f"... (up to {max_len})")


#── Variation: Minimum Number of Cables (Maximum length when divided into K pieces) ────────────────────────────

def demo_lanline() -> None:
    """Baekjoon 1654 (Cutting LAN Cables) style."""
    cables = [802, 743, 457, 539]
    k = 11

    print("=== Cable Cutting (Baekjoon 1654 style) ===")
    answer = max_length_for_k(cables, k)
    pieces = count_pieces(cables, answer)
    print(f"Cables: {cables}")
    print(f"Maximum length to create at least {k} pieces: {answer}cm")
    print(f"Actual number of pieces obtained: {pieces}")


#── Variation: Cutting Trees ────────────────────────────────────────────────────

def demo_tree_cutting() -> None:
    """Baekjoon 2805 (Cutting Trees) style."""
    trees = [20, 15, 10, 17]
    need = 7

    def tree_feasible(h: int) -> bool:
        return sum(max(0, t - h) for t in trees) >= need

    left, right = 0, max(trees)
    answer = 0
    steps = []
    while left <= right:
        mid = (left + right) // 2
        collected = sum(max(0, t - mid) for t in trees)
        ok = tree_feasible(mid)
        steps.append((left, mid, right, collected, ok))
        if ok:
            answer = mid
            left = mid + 1
        else:
            right = mid - 1

    print("\n=== Cutting Trees (Baekjoon 2805 style) ===")
    print(f"Trees: {trees}, Needed amount: {need}")
    print(f"{'left':>5} {'mid':>5} {'right':>6} {'Collected':>6} {'Possible?':>6}")
    for l, m, r, c, ok in steps:
        print(f"{l:>5} {m:>5} {r:>6} {c:>6} {'✅' if ok else '❌'}")
    print(f"→ Cutting height H = {answer}")


#── Main ─────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    cables = [10, 15, 20, 25]
    k = 8

    print("=" * 50)
    print("Cable cutting step visualization")
    print("=" * 50)
    visualize_cutting(cables, k)
    answer = max_length_for_k(cables, k)
    print(f"→ Maximum length = {answer}, Number of pieces = {count_pieces(cables, answer)}")

    demo_lanline()
    demo_tree_cutting()

    #Validation
    assert max_length_for_k([802, 743, 457, 539], 11) == 200
    assert max_length_for_k([20, 15, 10, 17], 7) == 15  #Cutting trees H=15 → 7
    print("Accuracy check: PASS")
src/03_book_pages.pyPython
"""Topic: Book Page Allocation — Minimize Maximum
Time: O(N log(sum))
Space: O(N)

Problem: When dividing N chapters among K readers,
         minimize the maximum number of pages any one person has to read.
         (Maintain chapter order, only consecutive sections allowed)"""


#── Decision Function ─────────────────────────────────────────────────────────

def can_distribute(chapters: list[int], k: int, max_pages: int) -> bool:
    """Can the chapters be divided among K or fewer people under a max_pages limit?

    Each reader must read consecutive chapters."""
    readers = 1
    current = 0
    for pages in chapters:
        if pages > max_pages:
            return False           #Single chapter exceeds limit
        if current + pages > max_pages:
            readers += 1
            current = pages
            if readers > k:
                return False
        else:
            current += pages
    return True


#── Parameter Search ────────────────────────────────────────────────────────────

def min_max_pages(chapters: list[int], k: int) -> int:
    """Minimum maximum number of pages when dividing among K people.

    Search range:
      lo = max(chapters)  : each person gets one chapter when K=N
      hi = sum(chapters)  : one person reads all when K=1"""
    lo = max(chapters)
    hi = sum(chapters)
    answer = hi

    while lo <= hi:
        mid = (lo + hi) // 2
        if can_distribute(chapters, k, mid):
            answer = mid
            hi = mid - 1    #Try a smaller value
        else:
            lo = mid + 1    #Needs to be increased

    return answer


#── Visualization ───────────────────────────────────────────────────────────────────

def visualize_distribution(
    chapters: list[int], k: int, max_pages: int
) -> None:
    """Print a way to divide among K people under a max_pages limit."""
    readers: list[list[int]] = [[]]
    current = 0
    for ch in chapters:
        if current + ch > max_pages:
            readers.append([])
            current = 0
        readers[-1].append(ch)
        current += ch

    print(f"Distribute to {len(readers)} people with max_pages={max_pages}:")
    for i, r in enumerate(readers):
        print(f"Reader {i+1}: {r}  (Sum={sum(r)})")
    print(f"→ {'Possible ✅' if len(readers) <= k else 'Not possible ❌'}")


#── Main ─────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    chapters = [100, 200, 300, 400, 500, 600, 700]
    k = 3

    print("=" * 55)
    print("Book page distribution — Minimize maximum pages for K people")
    print("=" * 55)
    print(f"Chapter pages: {chapters}")
    print(f"Number of readers K: {k}")
    print(f"lo         : {max(chapters)} (minimum if each handles one chapter)")
    print(f"hi         : {sum(chapters)} (maximum if one reads all)")

    #Check for monotonicity
    lo, hi = max(chapters), sum(chapters)
    sample_points = range(lo, hi + 1, (hi - lo) // 20 or 1)
    print(f"{'max_pages':>10}  {'readers':>8}  {'Possible?':>6}")
    for mp in sample_points:
        readers_needed = 1
        cur = 0
        for ch in chapters:
            if cur + ch > mp:
                readers_needed += 1
                cur = ch
            else:
                cur += ch
        ok = readers_needed <= k
        print(f"{mp:>10}  {readers_needed:>8}  {'✅' if ok else '❌'}")

    answer = min_max_pages(chapters, k)
    print(f"→ Minimum = {answer} pages")
    visualize_distribution(chapters, k, answer)

    #Test various k values
    print("=== Minimum maximum pages by K ===")
    for ki in range(1, len(chapters) + 1):
        result = min_max_pages(chapters, ki)
        print(f"  K={ki}: {result}")

    #Verify accuracy
    assert min_max_pages([1, 2, 3, 4, 5], 2) == 9   # [1,2,3] [4,5]
    assert min_max_pages([1, 2, 3, 4, 5], 5) == 5   #Each person one
    assert min_max_pages([1, 2, 3, 4, 5], 1) == 15  #One person all
    print("Accuracy check: PASS")
src/04_kth_smallest.pyPython
"""Topic: The k-th smallest number in a multiplication table (parametric search)
Time: O(m log(m*n))   m*n = table size
Space: O(1)

Problem: What is the k-th smallest number in an m×n multiplication table?
Multiplication table[i][j] = i*j (1 ≤ i ≤ m, 1 ≤ j ≤ n)

Decision function: "Are there at least k numbers less than or equal to x?"
Number of numbers less than or equal to x = sum(min(x//i, n) for i in range(1, m+1))"""

import math


#── Decision Function ─────────────────────────────────────────────────────────

def count_leq(m: int, n: int, x: int) -> int:
    """The number of numbers less than or equal to x in an m×n multiplication table.

    Number of j in row i satisfying i*j ≤ x = min(x//i, n)"""
    count = 0
    for i in range(1, m + 1):
        count += min(x // i, n)
    return count


def find_kth_in_multiplication_table(m: int, n: int, k: int) -> int:
    """The k-th smallest number in an m×n multiplication table.

    Search range: [1, m*n]"""
    lo, hi = 1, m * n
    while lo < hi:
        mid = (lo + hi) // 2
        if count_leq(m, n, mid) >= k:
            hi = mid          #If there are k or fewer elements below mid, reduce hi
        else:
            lo = mid + 1
    return lo


#── Brute-force verification ───────────────────────────────────────────────────────────

def brute_force_kth(m: int, n: int, k: int) -> int:
    """O(mn log mn) brute-force solution calculation."""
    table = sorted(i * j for i in range(1, m + 1) for j in range(1, n + 1))
    return table[k - 1]


#── Visualization ───────────────────────────────────────────────────────────────────

def print_multiplication_table(m: int, n: int) -> None:
    """Print multiplication table."""
    print(f"{m}×{n} multiplication table:")
    header = "   " + "  ".join(f"{j:3d}" for j in range(1, n + 1))
    print(header)
    for i in range(1, m + 1):
        row = f"{i:2d} |" + "  ".join(f"{i*j:3d}" for j in range(1, n + 1))
        print(row)


def visualize_kth_search(m: int, n: int, k: int) -> None:
    """Visualization of the search process."""
    lo, hi = 1, m * n
    print(f"{'lo':>5} {'mid':>5} {'hi':>5} {'count_leq':>10} {'direction':>6}")
    while lo < hi:
        mid = (lo + hi) // 2
        cnt = count_leq(m, n, mid)
        direction = "hi=mid" if cnt >= k else "lo=mid+1"
        print(f"{lo:>5} {mid:>5} {hi:>5} {cnt:>10} {direction}")
        if cnt >= k:
            hi = mid
        else:
            lo = mid + 1
    print(f"→ k-th = {lo}")


#── Router installation (maximum of minimum) ────────────────────────────────────────────

def max_min_distance(houses: list[int], c: int) -> int:
    """Maximize the minimum distance between C installed routers.

    Baekjoon 2110 style.
    Decision function: Can you install C routers while maintaining a minimum distance d?"""
    houses_sorted = sorted(houses)

    def feasible(d: int) -> bool:
        count = 1
        last = houses_sorted[0]
        for h in houses_sorted[1:]:
            if h - last >= d:
                count += 1
                last = h
                if count >= c:
                    return True
        return count >= c

    lo, hi = 1, houses_sorted[-1] - houses_sorted[0]
    answer = 0
    while lo <= hi:
        mid = (lo + hi) // 2
        if feasible(mid):
            answer = mid
            lo = mid + 1
        else:
            hi = mid - 1
    return answer


def demo_router() -> None:
    """Router installation demo."""
    houses = [1, 2, 8, 4, 9]
    c = 3
    result = max_min_distance(houses, c)
    print(f"=== Router installation (Baekjoon 2110 style) ===")
    print(f"House locations: {sorted(houses)}")
    print(f"Number of routers C={c}")
    print(f"Maximum of minimum: {result}")


#── Main ─────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    m, n = 3, 3
    print_multiplication_table(m, n)

    print(f"=== k-th smallest number in {m}×{n} table ===")
    for k in range(1, m * n + 1):
        fast = find_kth_in_multiplication_table(m, n, k)
        brute = brute_force_kth(m, n, k)
        status = "✓" if fast == brute else "✗"
        print(f"  k={k:2d}: {fast:>4} {status}")

    print()
    m2, n2 = 5, 5
    print_multiplication_table(m2, n2)
    k2 = 10
    print(f"{k2}-th smallest number in {m2}×{n2} table:")
    visualize_kth_search(m2, n2, k2)

    #Large-scale verification
    large_result = find_kth_in_multiplication_table(100, 100, 5000)
    print(f"100×100 table 5000th: {large_result}")

    demo_router()

    #Verify accuracy
    assert find_kth_in_multiplication_table(3, 3, 5) == 4
    assert max_min_distance([1, 2, 8, 4, 9], 3) == 3
    print("Accuracy check: PASS")

Assignment

Solve optimization problems by combining decision functions and binary search.


Problem List

# Problem Tier Key Skill
1 Baekjoon 2805 – Cutting Trees Silver II Maximum value search
2 Baekjoon 2110 – Installing Routers Gold IV Maximum of the minimum value

Problem 1: Baekjoon 2805 – Cutting Trees (Silver II)

Problem Description

When cutting N trees at height H, to collect at least M meters, what is the maximum H?

Designing the Decision Function

feasible(H): Is it possible to collect at least M meters when cut to H?
  = sum(max(0, tree - H) for tree in trees) >= M

Checking Monotonicity

The larger H is, the less is collected
H=0: collect all (always ✅)
H=max: no collection (always ❌ if M > 0)

Search Direction

if feasible(mid):
    answer = mid
    left = mid + 1   #Try increasing H (searching for the maximum value)
else:
    right = mid - 1  #H needs to be lowered

Notes

  • Include H=0 in the search range (left=0)
  • No overflow issue when calculating collected amount (Python)

Problem 2: Baekjoon 2110 – Installing Routers (Gold IV)

Problem Description

Among N houses, when installing routers in C houses, maximize the minimum distance between adjacent routers.

Designing the Decision Function

feasible(d): Is it possible to install C units while maintaining a minimum distance of d?
- After sorting the houses, install them greedily
- Install at the first house that is at least d away from the last installation location

Search Direction

#Maximum of the minimum → Maximize search
if feasible(mid):
    answer = mid
    left = mid + 1   #Tried to increase the distance
else:
    right = mid - 1  #Need to reduce the distance

Search Range

  • lo = 1 (minimum adjacent distance)
  • hi = last house - first house (after sorting)

Implementing the Greedy Decision Function

def feasible(d: int) -> bool:
    count = 1
    last = houses[0]  #Install in the first house without fail
    for h in houses[1:]:
        if h - last >= d:
            count += 1
            last = h
    return count >= c

Common Notes

  • Did you set the search range sufficiently large?
  • Did you check the monotonicity of the decision function?
  • Is the search direction of Maximum/Minimum correct?
  • Did you set the initial value of answer?
  • Did you use input/output optimization (sys.stdin)?

Model Answer

Refer to the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

boj_2110.pyPython
"""Baekjoon 2110 – Router Installation
https://www.acmicpc.net/problem/2110
Time  : O(N log N + N log(max_dist))
Space : O(N)

Strategy: Maximum of the minimum values → Parametric search
Decision function: Is it possible to install C routers while maintaining a minimum distance of d?"""

import sys
input = sys.stdin.readline


def main() -> None:
    n, c = map(int, input().split())
    houses = sorted(int(input()) for _ in range(n))

    def feasible(d: int) -> bool:
        """Is it possible to install C routers while maintaining a minimum distance of d?"""
        count = 1
        last = houses[0]
        for h in houses[1:]:
            if h - last >= d:
                count += 1
                last = h
                if count >= c:
                    return True
        return count >= c

    left, right = 1, houses[-1] - houses[0]
    answer = 0
    while left <= right:
        mid = (left + right) // 2
        if feasible(mid):
            answer = mid
            left = mid + 1      #Tried to increase the distance
        else:
            right = mid - 1     #Need to reduce the distance

    print(answer)


if __name__ == "__main__":
    main()
boj_2805.pyPython
"""Baekjoon 2805 – Cutting Trees
https://www.acmicpc.net/problem/2805
Time  : O(N log(max_height))
Space : O(N)"""

import sys
input = sys.stdin.readline


def main() -> None:
    n, m = map(int, input().split())
    trees = list(map(int, input().split()))

    def feasible(h: int) -> bool:
        return sum(t - h for t in trees if t > h) >= m

    left, right = 0, max(trees)
    answer = 0
    while left <= right:
        mid = (left + right) // 2
        if feasible(mid):
            answer = mid
            left = mid + 1
        else:
            right = mid - 1

    print(answer)


if __name__ == "__main__":
    main()

Learn this lesson with a mentor

Work through the Python Algorithms & Data Structures track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now