耍杂技的牛
题目描述
农民约翰的 \(N\) 头奶牛(编号为 \(1..N\))计划逃跑并加入马戏团,为此它们决定练习表演杂技。
奶牛们不是非常有创意,只提出了一个杂技表演:
叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。
奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。
这 \(N\) 头奶牛中的每一头都有着自己的重量 \(W_i\) 以及自己的强壮程度 \(S_i\)。
一头牛支撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。
您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。
输入格式
第一行输入整数 \(N\),表示奶牛数量。
接下来 \(N\) 行,每行输入两个整数,表示牛的重量和强壮程度,第 i 行表示第 i 头牛的重量 \(W_i\) 以及它的强壮程度 \(S_i\)。
输出格式
输出一个整数,表示最大风险值的最小可能值。
数据范围
\(1≤N≤50000\),
\(1≤Wi≤10,000\),
\(1≤Si≤1,000,000,000\)
输入样例:
3
10 3
2 5
3 3
输出样例:
2
算法思路
假设牛按照初始顺序堆叠起来,从上到下编号依次为1、2、3、4.......
不失一般性地取出其中两头牛\(i、i+1\),对于这两头相邻的牛,存在以下性质
性质1:如果\(W_{i+1}+S_{i+1} < W_i + S_i\),那么将两头牛更换位置,整个堆叠的最大危险值不会比更换前更大
证明:\(max(i) \ge max(i+1)\)
i处危险值 | i+1处危险值 | 最大风险值 | |
---|---|---|---|
交换前 | \(W_1+W_2+...+W_{i-1}-S_i\) | \(W_1+W_2+...+W_i-S_{i+1}\) | \(max(I)\) |
交换后 | \(W_1+W_2+...+W_{i-1}-S_{i+1}\) | \(W_1+W_2+...+W_{i-1}+W_{i+1}-S_i\) | \(max(I+1)\) |
证明步骤
1、对比交换前i+1处危险值和交换后i处危险值,可知交换前的比交换后的多一个\(W_i\),由题知:\(W_i \ge 1\),所以可得\(交换后i处危险值 \le 交换前i+1处危险值\)
2、再对比交换前i+1处危险值和交换后i+1处危险值,可知两者差异为\(W_i-S_{i+1}\)和\(W_{i+1}-S_i\),又由前提知\(W_{i+1}+S_{i+1} < W_i + S_i\),交换可得\(W_{i+1}-S_i < W_i - S_{i+1}\),所以可知\(交换后i+1处危险值 \le 交换前i+1处危险值\)
3、又因为\(交换前i+1处危险值 \le max(i)\),且\(max(i+1) = 换后i处或i+1处危险值\),因此\(max(i+1) \le max(i)\)
得证
又因为假如存在一个最优解不满足\(W_{i+1}+S_{i+1} > W_i + S_i\)性质,那么根据性质1
,必然可以对其进行调节交换,使其形成的堆叠最终满足这个性质,而且最大危险值不会变大
有了这个性质,我们就可以根据\(W_i+S_i\)的值,对整个堆叠进行排序,最终按照这个值从小到大排成堆叠就是一个最优解
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 5e4 + 10;
PII a[N];
int n;
int main(){
cin >> n;
for(int x = 0; x < n; x++){
int w, s;
cin >> w >> s;
a[x] = {w + s, w};
}
sort(a, a + n);
int ans = -1e9, weight = 0;
for(int x = 0; x < n; x++){
auto c = a[x];
ans = max(ans, weight - c.first + c.second);
weight += c.second;
}
cout << ans;
}