P1842 [USACO05NOV]奶牛玩杂技
题意
Luogu P1842 \(_{这两天光做数据结构紫板子, 来水一道黄的摸摸鱼}\)
一只奶牛有两个属性, 力量 Stg
和质量 Weg
. 将奶牛摞成一摞, 一只奶牛的压扁指数是其上所有奶牛质量和减它自己的力量, 求奶牛中压扁指数最大值的最小值.
分析
最大值最小, 一上来就写了个二分答案, 在想 Judge
函数怎么写的时候, 推式子直接推出了贪心策略, 遂大喜, 用贪心打了一遍过了, 以下是推式子过程.
两个属性都大的必在下
首先, 考虑两只相邻的牛. 如果一只牛 Stg
和 Weg
都比另一头大, 那它必然要放在下面, 因为如果换过来, 两头牛中最扁的会再扁 \(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\), 所以表达式恒为正, 所以 Stg
和 Weg
都相对大的牛要在下面.
推广
考虑牛 \(2\) 放在牛 \(1\) 上的条件.
如果将上面的式子简单整理, 就会得到:
已知 \(Stg2 < Weg2 + Stg2\), \(Stg1 < Weg1 + Stg1\)
可以看出, \(Stg2\) 要小于 \(Weg1 + Stg1\), 否则, 就可以整理出 \(Stg2 > Weg2 + Stg2\) (不成立).
得到:
\(-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;
}