题解:洛谷 P1842 奶牛玩杂技

【题目来源】

洛谷:P1842 [USACO05NOV] 奶牛玩杂技 - 洛谷

【题目描述】

Farmer John 养了 \(N\) 头牛,她们已经按 \(1\sim N\) 依次编上了号。FJ 所不知道的是,他的所有牛都梦想着从农场逃走,去参加马戏团的演出。可奶牛们很快发现她们那笨拙的蹄子根本无法在钢丝或晃动的的秋千上站稳(她们还尝试过把自己装在大炮里发射出去,但可想而知,结果是悲惨的)。最终,她们决定练习一种最简单的杂技:把所有牛都摞在一起,比如说,第一头牛站在第二头的身上,同时第二头牛又站在第三头牛的身上...最底下的是第 \(N\) 头牛。

每头牛都有自己的体重以及力量,编号为 \(i\) 的奶牛的体重为 \(W_i\),力量为 \(S_i\)

当某头牛身上站着另一些牛时它就会在一定程度上被压扁,我们不妨把它被压扁的程度叫做它的压扁指数。对于任意的牛,她的压扁指数等于摞在她上面的所有奶牛的总重(当然不包括她自己)减去它的力量。奶牛们按照一定的顺序摞在一起后,她们的总压扁指数就是被压得最扁的那头奶牛的压扁指数。

你的任务就是帮助奶牛们找出一个摞在一起的顺序,使得总压扁指数最小。

【输入】

第一行一个整数 \(N\)

接下来 \(N\) 行,每行两个整数 \(W_i\)\(S_i\)

【输出】

一行一个整数表示最小总压扁指数。

【输入样例】

3
10 3
2 5
3 3

【输出样例】

2

【核心思想】

  1. 问题分析:给定 \(N\) 头牛,每头牛有体重 \(W_i\) 和力量 \(S_i\)。将它们按某种顺序从下到上叠放,每头牛的压扁指数 = 上方所有牛的总重 \(-\) 该牛的力量。求所有叠放顺序中,最大压扁指数的最小值。这是一个贪心排序问题,关键在于发现按 \(W_i + S_i\) 升序排列可使最大压扁指数最小。

  2. 算法选择

    • 贪心策略:按 \(W_i + S_i\)(体重与力量之和)从小到大排序,该顺序即为最优叠放顺序
    • 风险值计算:排序后从下往上叠放,维护上方累积重量 \(t\),每头牛的压扁指数为 \(t - S_i\),取全局最大值
  3. 关键步骤

    • 读取输入\(N\) 和每头牛的 \((W_i, S_i)\)
    • 贪心排序:按 \(W_i + S_i\) 升序排列
    • 计算最大压扁指数
      • 初始化 \(t = 0\)(上方累积重量),\(res = -\infty\)
      • 遍历排序后的牛(从下到上):
        • \(res = \max(res,\ t - S_i)\):当前牛的压扁指数
        • \(t \mathrel{+}= W_i\):将该牛重量加入上方累积重量
    • 输出 \(res\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),排序主导
    • 空间复杂度:\(O(N)\),存储牛的信息
  5. 贪心排序的核心思想

    • 交换论证:考虑相邻两头牛 \(i\)\(j\)\(i\) 在下,\(j\) 在上),交换它们不影响其他牛的压扁指数。通过比较交换前后的最大压扁指数,可证明当 \(W_i + S_i \le W_j + S_j\) 时,原顺序不劣于交换后的顺序
    • 局部最优推全局最优:相邻交换的贪心选择性质保证了按 \(W + S\) 排序的全局最优性
    • 风险累积\(t\) 单调递增,力量 \(S_i\) 较大的牛应放在下方(此时 \(t\) 较小),以减小 \(t - S_i\)
    • 适用于"排序后单调累积求最值"类问题,核心是通过交换论证发现正确的排序关键字

【算法标签】

普及 #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 50004;  // 定义最大牛的数量
int n;                    // 牛的数量
int res = -1e9;           // 存储最终结果(最大风险值)
int t;                    // 当前累积的重量

// 定义牛的结构体,包含重量和力量
struct Node
{
    int w;  // 牛的重量
    int s;  // 牛的力量

    // 重载小于运算符,用于排序
    bool operator < (Node &t)
    {
        return w + s < t.w + t.s;
    }
} a[MAX_N];  // 存储所有牛的信息

int main()
{
    // 输入牛的数量
    cin >> n;

    // 输入每头牛的重量和力量
    for (int i = 1; i <= n; i++)
        cin >> a[i].w >> a[i].s;

    // 按照w+s升序排序
    sort(a + 1, a + n + 1);

    // 计算最大风险值
    for (int i = 1; i <= n; i++)
    {
        // 当前牛的风险值 = 上方所有牛的重量 - 当前牛的力量
        res = max(res, t - a[i].s);

        // 累加当前牛的重量
        t += a[i].w;
    }

    // 输出结果
    cout << res << endl;

    return 0;
}

【运行结果】

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