题目地址:
https://www.acwing.com/problem/content/127/
有 N N N头奶牛,编号 1 ∼ N 1\sim N 1∼N,每个奶牛有两个属性,自己的重量 W i W_i Wi和其强壮程度 S i S_i Si。它们要表演叠罗汉,一个叠在另一个上面,每个奶牛的风险值定义为其上方的所有奶牛的重量之和减去其自己的强壮程度。问如何安排叠罗汉次序可以使得风险值最大的那个奶牛的风险值最小。
输入格式:
第一行输入整数
N
N
N,表示奶牛数量。接下来
N
N
N行,每行输入两个整数,表示牛的重量和强壮程度,第
i
i
i行表示第
i
i
i头牛的重量
W
i
W_i
Wi以及它的强壮程度
S
i
S_i
Si。
输出格式:
输出一个整数,表示最大风险值的最小可能值。
数据范围:
1
≤
N
≤
50000
1\le N\le 50000
1≤N≤50000
1
≤
W
i
≤
10000
1\le W_i\le 10000
1≤Wi≤10000
1
≤
S
i
≤
1
0
9
1\le S_i\le 10^9
1≤Si≤109
可以将奶牛按照 W i + S i W_i+S_i Wi+Si从小到大排序,小的在上,大的在下,进行叠罗汉。此时最大风险值最小。
算法正确性证明:
设最优解不是上述所说的,那么一定存在两头相邻的奶牛,满足
W
i
+
1
+
S
i
+
1
<
W
i
+
S
i
W_{i+1}+S_{i+1}<W_i+S_i
Wi+1+Si+1<Wi+Si,那么位于位置
i
i
i的奶牛的风险值是
W
1
+
.
.
.
+
W
i
−
1
−
S
i
W_1+...+W_{i-1}-S_i
W1+...+Wi−1−Si,而位于位置
i
+
1
i+1
i+1的奶牛的风险值是
W
1
+
.
.
.
+
W
i
−
S
i
+
1
W_1+...+W_i-S_{i+1}
W1+...+Wi−Si+1;如果交换这两头奶牛,那么这时位于位置
i
i
i的奶牛的风险值是
W
1
+
.
.
.
+
W
i
−
1
−
S
i
+
1
W_1+...+W_{i-1}-S_{i+1}
W1+...+Wi−1−Si+1,位于位置
i
+
1
i+1
i+1的奶牛的风险值是
W
1
+
.
.
.
+
W
i
−
1
+
W
i
+
1
−
S
i
W_1+...+W_{i-1}+W_{i+1}-S_i
W1+...+Wi−1+Wi+1−Si,我们只需比较
max
{
−
S
i
,
W
i
−
S
i
+
1
}
\max\{-S_i,W_i-S_{i+1}\}
max{−Si,Wi−Si+1}和
max
{
−
S
i
+
1
,
W
i
+
1
−
S
i
}
\max\{-S_{i+1},W_{i+1}-S_i\}
max{−Si+1,Wi+1−Si},由于
W
i
+
1
+
S
i
+
1
<
W
i
+
S
i
W_{i+1}+S_{i+1}<W_i+S_i
Wi+1+Si+1<Wi+Si,所以
W
i
+
1
−
S
i
<
W
i
−
S
i
+
1
W_{i+1}-S_i<W_i-S_{i+1}
Wi+1−Si<Wi−Si+1,又
W
i
>
0
W_i>0
Wi>0,所以
−
S
i
+
1
<
W
i
−
S
i
+
1
-S_{i+1}<W_i-S_{i+1}
−Si+1<Wi−Si+1,所以
max
{
−
S
i
+
1
,
W
i
+
1
−
S
i
}
<
W
i
−
S
i
+
1
≤
max
{
−
S
i
,
W
i
−
S
i
+
1
}
\max\{-S_{i+1},W_{i+1}-S_i\}<W_i-S_{i+1}\le \max\{-S_i,W_i-S_{i+1}\}
max{−Si+1,Wi+1−Si}<Wi−Si+1≤max{−Si,Wi−Si+1},可以看出换位子之后,最大风险值会变小或者不变。既然当前解就是最优解,所以换位子之后最大风险值应当是不变的,那我们重复这样的操作,不停交换这样的逆序对,直到从上到下是按照
W
i
+
S
i
W_i+S_i
Wi+Si从小到大排列为止(这是冒泡排序,是可以做到的),说明这样排列也是最优解,所以算法正确。
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50010;
pair<int, int> a[N];
// 重新定义pair<int, int>的比较函数(定义小于)
bool compare(const pair<int, int>& p1, const pair<int, int>& p2) {
return p1.first + p1.second < p2.first + p2.second;
}
int main() {
int n;
cin >> n;
// first是重量,second是强壮程度
for (int i = 0; i < n; i++) cin >> a[i].first >> a[i].second;
sort(a, a + n, compare);
int res = -2e9, sum = 0;
for (int i = 0; i < n; i++) {
res = max(res, sum - a[i].second);
sum += a[i].first;
}
cout << res << endl;
return 0;
}
时间复杂度 O ( N log N ) O(N\log N) O(NlogN),空间 O ( 1 ) O(1) O(1)。