题解:洛谷 P1824 Aggressive cows

【题目来源】

洛谷:P1824 [USACO05FEB] 进击的奶牛 Aggressive Cows G - 洛谷

【题目描述】

农夫约翰建造了一座有 \(n\) 间牛舍的小屋,牛舍排在一条直线上,第 \(i\) 间牛舍在 \(x_i\) 的位置,但是约翰的 \(m\) 头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍。也就是要最大化最近的两头牛之间的距离。

牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗。为了不让牛互相伤害。约翰决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢?

【输入】

第一行用空格分隔的两个整数 \(n\)\(m\)

下面 \(n\) 行为 \(n\) 个用空格隔开的整数,表示位置 \(x_i\)

【输出】

一行一个整数,表示最大的最小距离值。

【输入样例】

5 3
1 2 8 4 9

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(n\) 个牛舍的直线位置和 \(m\) 头牛,需要将 \(m\) 头牛放入牛舍,使得任意两头牛之间的最小距离最大化。这是一个整数二分 + 贪心验证问题,关键在于将"最大化最小距离"转化为二分判定问题,用贪心策略验证给定距离 \(d\) 是否可行。

  2. 算法选择

    • 整数二分:在距离范围 \([1, \max(x_i) - \min(x_i)]\) 内二分查找最大的可行最小距离
    • 贪心验证:对于给定的候选距离 \(mid\),从左到右贪心放置牛:第一头放最左,之后每头放在距离上一头至少 \(mid\) 的最左位置,统计能放多少头
  3. 关键步骤

    • 读取输入\(n, m\)\(n\) 个位置 \(x_i\)
    • 排序sort(a+1, a+n+1),使位置有序
    • 二分初始化\(l = 1\)\(r = a[n] - a[1]\)(最大可能距离)
    • 二分过程\(l < r\)):
      • \(mid = l + (r - l) / 2\)
      • 贪心验证
        • last = a[1]\(cnt = 1\)(第一头放最左)
        • 遍历 \(i\)\(2\)\(n\):若 \(a[i] - last \ge mid\),则 \(cnt \mathrel{+}= 1\)last = a[i]
      • 判定
        • \(cnt \ge m\):距离 \(mid\) 可行,尝试更大,\(l = mid + 1\)
        • \(cnt < m\):距离 \(mid\) 不可行,尝试更小,\(r = mid\)
    • 输出 \(l - 1\)(最终 \(l\) 指向第一个不可行的距离)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log (\max x_i))\),二分 \(O(\log (\max x_i))\) 轮,每轮贪心验证 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储位置数组
  5. 整数二分 + 贪心验证的核心思想

    • 单调性:若距离 \(d\) 可行(能放 \(m\) 头牛),则所有 \(\le d\) 的距离也可行;若 \(d\) 不可行,则所有 \(> d\) 的距离均不可行。满足二分条件
    • 贪心最优性:对于固定的 \(d\),从左到右尽可能早地放牛,能为后续留下更多空间,是验证 \(d\) 是否可行的最优策略
    • 最大化最小值的经典框架:"最大化最小距离/最小化最大距离"类问题通常转化为二分 + 贪心验证
    • 适用于"资源分配 + 间距约束"类问题,核心是利用单调性二分搜索并用贪心验证可行性

【解题思路】

【算法标签】

普及- #整数二分

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, m, l, r, mid;
int a[100005];
int main()
{
    cin >> n >> m;  // 输入n和m
    for (int i=1; i<=n; i++) {  // 输入n个牛舍的位置
        cin >> a[i];
    }
    sort(a+1, a+n+1);  // 按照从小到大排列,因为后面需要二分搜索
    l = a[1], r = a[n];  // 定义l和r
    while (l<r) {  // 二分模板
        int cnt=0;  // 统计可选择的牛舍
        mid = l + (r-l)/2;  // 二分模板
        int last = a[1];  // 定义待比较的牛舍,初始从第1个牛舍开始比较
        for (int i=2; i<=n; i++) {  // 往后遍历所有牛舍
            if (a[i]-last>=mid) {  // 如果两个牛舍之间的距离大于等于mid,即可以被选择
                cnt++;  // 统计数自增1
                last = a[i];
            }
        }
        // cout << "cnt " << cnt << endl;
        if (cnt<m-1) r = mid;
        else l = mid+1;
    }
    cout << l-1 << endl;  // 因为r先动,所以最终输出应该是l-1(l最后+1试探失败,所以需要l-1,即为mid)
    return 0;
}

【运行结果】

5 3
1 2 8 4 9
3
posted @ 2026-08-28 15:54  团爸讲算法  阅读(3)  评论(0)    收藏  举报