P1842 [USACO05NOV]奶牛玩杂技

题意

Luogu P1842 \(_{这两天光做数据结构紫板子, 来水一道黄的摸摸鱼}\)

一只奶牛有两个属性, 力量 Stg 和质量 Weg. 将奶牛摞成一摞, 一只奶牛的压扁指数是其上所有奶牛质量和减它自己的力量, 求奶牛中压扁指数最大值的最小值.

分析

最大值最小, 一上来就写了个二分答案, 在想 Judge 函数怎么写的时候, 推式子直接推出了贪心策略, 遂大喜, 用贪心打了一遍过了, 以下是推式子过程.

两个属性都大的必在下

首先, 考虑两只相邻的牛. 如果一只牛 StgWeg 都比另一头大, 那它必然要放在下面, 因为如果换过来, 两头牛中最扁的会再扁 \(max(-Stg1, Weg1 - Stg2) - max(-Stg2, Weg2 - Stg1)\)

因为 \(-Stg1 < Weg1 - Stg2\), 所以表达式变成 \(Weg1 - Stg2 - max(-Stg2, Weg2 - Stg1)\), 整理得 \(max(Weg1, Weg1 - Weg2 + Stg1 - Stg2)\)

由于 \(Stg1 > Stg2\), \(Weg1 > Wei2\), 所以表达式恒为正, 所以 StgWeg 都相对大的牛要在下面.

推广

考虑牛 \(2\) 放在牛 \(1\) 上的条件.

\[max(-Stg1, Weg1 - Stg2) - max(-Stg2, Weg2 - Stg1) > 0 \]

如果将上面的式子简单整理, 就会得到:

\[max(Stg2, Weg1 + Stg1) > max(Stg1, Weg2 + Stg2) \]

已知 \(Stg2 < Weg2 + Stg2\), \(Stg1 < Weg1 + Stg1\)

可以看出, \(Stg2\) 要小于 \(Weg1 + Stg1\), 否则, 就可以整理出 \(Stg2 > Weg2 + Stg2\) (不成立).

得到:

\[max(-Weg1, Weg2 + Stg2 - Weg1 - Stg1) < 0 \]

\(-Weg1\) 必然是负数, 可知下面的牛 \(Stg + Weg\) 的和尽可能大, 推得上面的牛 \(Stg + Weg\) 尽量小, \(Stg + Weg\) 相同时, \(Weg\) 大的在下.

贪心策略就是以 \(Stg + Weg\) 为第一关键字, 升序排序, 以 \(Weg\) 为第二关键字, 升序排序.

实现

结构体 + 重载运算符 + sort, 最后 \(O(n)\) 统计答案.

unsigned int n, tmp(0) /*质量前缀和*/;
int ans(-0x3f3f3f3f);  //初始值足够小
struct Cow {
  unsigned int Weg, Stg;
  const bool operator<(const Cow &x) const {
    if (this->Stg + this->Weg == x.Stg + x.Weg) {  //第一关键字相同
      return this->Weg < x.Weg;                    //以第二关键字为序
    }
    return this->Stg + this->Weg < x.Stg + x.Weg;  //以第一关键字为序
  }
} C[50005];
int main() {
  n = RD();
  for (register unsigned int i(1); i <= n; ++i) {
    C[i].Weg = RD();
    C[i].Stg = RD();
  }
  sort(C + 1, C + n + 1);  //按 Cow 的规则升序排序
  for (register unsigned int i(1); i <= n; ++i) {
    ans = max(ans, (int)tmp - (int)C[i].Stg);  //尝试更新答案
    tmp += C[i].Weg;                           //累计质量
  }
  printf("%d\n", ans);
  return 0;
}
posted @ 2021-02-02 08:53  Wild_Donkey  阅读(97)  评论(0编辑  收藏  举报