AcWing 125. 耍杂技的牛
农民约翰的 $N$ 头奶牛(编号为$1..N$)计划逃跑并加入马戏团,为此它们决定练习表演杂技。
奶牛们不是非常有创意,只提出了一个杂技表演:
叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。
奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。
这 $N$头奶牛中的每一头都有着自己的重量 $Wi$以及自己的强壮程度 $Si$。
一头牛支撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。
您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。
输入格式:
第一行输入整数 $N$,表示奶牛数量。
接下来 $N$行,每行输入两个整数,表示牛的重量和强壮程度,第 $i$行表示第$i$头牛的重量 $Wi$ 以及它的强壮程度 $Si$。
输出格式:
输出一个整数,表示最大风险值的最小可能值。
贪心策略进行猜想:
根据题意,我们可以得出危险值的公式如下:
$$每头牛的危险值D = 他前面牛的W(重量值)之和 - 自身的S(强壮值)$$
$$D=W-S$$
由此,我们根据贪心思想,首先应该就会想到,如果要所有奶牛的风险值中的最大值尽可能的小。那么就应该把$W$重量越重的越往底部堆叠
;并且$S$越强壮的越堆叠在下方
。因此,猜想$W+S$的值越大的应该越堆叠在下方。
分析证明:
如图所示,随机一种堆叠方式,那么何时我们才需要任意i和i+1的交换位置呢?
,那自然是任意位置$i$和$i+1$交换之后,可以降低这两个位置中风险值D的最大值
的情况下,才会进行交换。
由图中可知交换$i和i+1$的位置并不会影响其他任何位置的危险值。此时已经求出交换$i和i+1$的位置前后的危险值$D$已经求出,为了方便比较,此处将交换前后$D$中公共值都减去相同$\sum_{j=1}^{i-1} W_j$。
此时,只需要比较交换前后的值简化值即可。
交换前: $$i对应: \quad -S_i \quad (1) $$
$$i+1对应:\quad W_i-S_{i+1}\quad (2)$$
交换后: $$i对应: \quad W_{i+1}-S_i\quad (3)$$
$$i+1对应:\quad-S_{i+1} \quad (4)$$
达到降低这两个位置中风险值D的最大值
的效果,才会进行交换。即满足以下公式才进行交换
$$max[(1),(2)]<max[(3),(4)]$$
由于任意$W$和$S$都为正数很明显可以看出,$W_{i+1}-S_{i}> -S_i \quad和\quad W_i-S_{i+1}>-S_{i+1}$。
故只需比较$W_i-S_{i+1}$和$W_{i+1}-S_i$的大小即可。因为无外乎两种情况,一种是最大值有变大,另一种是最大值变小。
如果$W_i-S_{i+1}>W_{i+1}-S_i$,交换前后危险值的最大值$max(D_i,D_{i+1})$必然是变小了。因为$W_i-S_{i+1}>W_{i+1}-S_i$成立且$W_i-S_{i+1}>-S_{i+1}$,所以$W_i-S_{i+1}$大于交换后的所有值
,即$W_i-S_{i+1}$大于交换后的最大值
。此时,无论交换前$W_i-S_{i+1}$是最大值还是$-S_i$是最大值,都可以得出交换前的最大值大于交换后的最大值
,即进行交换后,危险值的最大值$max(D_i,D_{i+1})$降低了。
如果$W_i-S_{i+1}<W_{i+1}-S_i$,交换前后危险值的最大值$max(D_i,D_{i+1})$必然是变大了。因为$W_i-S_{i+1}<W_{i+1}-S_i$成立且$-S_i<W_{i+1}-S_{i}$,所以$W_{i+1}-S_{i}$大于交换前的所有值
,即$W_{i+1}-S_{i}$大于交换前的最大值
。此时,无论交换后$W_{i+1}-S_{i}$是最大值还是$-S_{i+1}$是最大值,都可以得出交换后的最大值大于交换前的最大值
,即进行交换后,危险值的最大值$max(D_i,D_{i+1})$提高了。
此时交换前后,危险值的最大值的比较问题已经解决。
回归到正题。那么何时我们才需要任意i和i+1的交换位置呢?
,当两个位置中风险值D的最大值降低
就进行交换。即需要满足$W_i-S_{i+1}>W_{i+1}-S_i$才进行交换。最终可得公式
$$W_i+S_i>W_{i+1}+S_{i+1}$$
只有满足上述公式时,才能保证任意i和i+1位置的奶牛交换后,危险值的最大值降低。以图中堆叠的视角来看,就是只有堆叠在上方的重量$W$和强壮值$S$大于堆叠在下方重量$W$和强壮值$S$时,才需要向下交换位置,从而一步一步实现危险值的最大值的最小情况,否则的话说明堆叠的情况已经是危险值最大值的最小情况了。
AC代码
经过上述分析后,得出规律W+S越大,越应该堆叠在下面是正确。由此得到以下AC代码:
#include<iostream>
#include<algorithm>
#include <climits>
using namespace std;
const int N = 50010;
struct COW
{
int w, s;
bool operator<(const COW& c)const {
return w + s < c.w + c.s;
}
}cow[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> cow[i].w >> cow[i].s;
//升序排序
sort(cow, cow + n);
//结果可能是负值,初始化为int最小值
int res = INT_MIN;
//记录某个奶牛的上方的累积重量
int weights = 0;
for (int i = 0; i < n; i++) {
res = max(res, weights - cow[i].s);
weights += cow[i].w;
}
cout << res << endl;
return 0;
}