「Luogu P2123」皇后游戏
题目
皇后有 \(n\) 位大臣,每位大臣的左右手上面分别写上了一个正整数。恰逢国庆节来临,皇后决定为 \(n\) 位大臣颁发奖金,其中第 \(i\) 位大臣所获得的奖金数目为第 \(i - 1\) 位大臣所获得奖金数目与前 \(i\) 位大臣左手上的数的和的较大值再加上第 \(i\) 位大臣右手上的数。
形式化地讲:我们设第 \(i\) 位大臣左手上的正整数为 \(a_i\),右手上的正整数为 \(b_i\),则第 \(i\) 位大臣获得的奖金数目为 \(c_i\) 可以表达为:
\[c_{i} = \begin{cases} a_{1}+b_{1} & ,i=1 \\ \displaystyle \max \left \{ c_{i-1},\sum_{j=1}^{i}a_{j} \right \} +b_{i} & ,2\leq i \leq n \end{cases} \]当然,吝啬的皇后并不希望太多的奖金被发给大臣,所以她想请你来重新安排一下队伍的顺序,使得获得奖金最多的大臣,所获奖金数目尽可能的少。
注意:重新安排队伍并不意味着一定要打乱顺序,我们允许不改变任何一位大臣的位置。
分析
可以发现这道题的套路和 国王游戏 很像, 于是我们尝试邻项交换推式子.
考虑当前排列的第 \(i\) 和第 \(i+1\) 位大臣. 记 \(S = \sum_{j=1}^{i-1}a_{j}\), \(c_{i}\) 和 \(c_{i}^{\prime}\) 分别为交换前和交换后排在第 \(i\) 位的大臣的奖金. 我们有
若不换更优的话, 就需要 \(c_{i+1} \leq c_{i+1}^{\prime}\), 即
拆开得
把公共项 \(c_{i-1}+b_{i}+b_{i+1}\) 去掉不影响原条件式成立的充分必要性 (想一想, 为什么?), 于是得到
相同项移到同一边有
这等价于
于是我们就得到了最终式, 直接 sort 就好了.
......等会, 这 cmp() 函数怎么感觉有点奇怪呢, 这真的能成功按照我们的想法 sort 吗? 嘛, 事实是, sort 能跑, 但是结果不尽如人意. 这时候就需要引入 Strict Weak Ordering 这个东西了 ┏ (゜ ω ゜)=👉
Strict Weak Ordering[1]
简单来说, STL 的任何比较函数 (包括但不限于使用
std::sort、std::lower_bound、std::priority_queue时重载的小于号), 都需要满足以下四个条件: (用 \(<\) 表示重载的运算符)
- \(x \not< x\) (非自反性)
- 若 \(x < y\), 则 \(y \not< x\) (非对称性)
- 若 \(x < y, y < z\), 则 \(x < z\) (传递性)
- 若 \(x \not< y, y \not< z\), 则 \(x \not< z\) (不可比性的传递性)
事实上可以由 1 和 3 推出 2.
而 \(\min\{a_{i+1}, b_{i}\} \geq \min\{a_{i}, b_{i+1}\}\) 这个式子并不满足条件 4. 事实上, 去掉 \(=\) 是能够满足上述条件的, 但是此时就会犯一个邻项交换排序中经典的错误. \(\min\{a_{i+1}, b_{i}\} > \min\{a_{i}, b_{i+1}\}\) 仅仅是我们不交换相邻两项的必要条件, 而不是充要条件, 仔细思考就会发现, 这种错误同样出自于缺失「不可比性的传递性」, 也就是说, 邻项交换排序的条件式同样需要满足 Strict Weak Ordering.
虽然按条件判断相等的两组数交换一次对后面确实不会产生影响,但可以通过多次交换对最终结果产生影响。[2]
这时候应该怎么办? 不妨考虑一下多次交换造成最终影响的来源是什么. 在本题中, \(a\) 数组的排序可能会截断 \(\sum b\), 如果 \(c_{i}\) 表达式中取 \(\max\) 的是 \(c_{i-1}\), 它必然会有以 \(i-1\) 为结尾的某一段 \(b\) 数组的后缀和, 而之后若某一次 \(\sum_{i=1}^{k} a_{j}\) 取到了 \(\max\), 便会在最终结果中消去前面所有 \(b\) 数组的影响, 而无论谁取到了 \(\max\), \(a\) 数组在之前产生的影响一定不会被消除, 那么我们必然希望较大的 \(a_{i}\) 放在后面来提高取消目前存在的 \(b\) 数组的影响的概率, 同时也能让前面可能造成对 \(c\) 的影响的 \(a\) 尽可能小. 因此当 \(\min\{a_{i+1}, b_{i}\} = \min\{a_{i}, b_{i+1}\}\) 时, 我们修改判定条件为 \(a_{i} < a_{i+1}\), 可以发现此时的判定式是满足 STL 的 Strict Weak Ordering 条件的.
当然, 还有一种按 \(d_{i} = \frac{a_{i} - b_{i}}{\lvert a_{i} - b_{i} \rvert}\) 排序[3]的, 同样用到了 \(a\) 小放前面, \(b\) 大的放前面的贪心思路, 但是感觉做法没有前面的那么自然 qwq.
代码
#include <bits/stdc++.h>
using i64 = long long;
using PLL = std::pair<i64, i64>;
void solve() {
int n;
std::cin >> n;
std::vector<PLL> pr(n);
for (int i = 0; i < n; ++i) {
std::cin >> pr[i].first >> pr[i].second;
}
std::sort(pr.begin(), pr.end(), [&](PLL x, PLL y) -> bool {
i64 u = std::min(y.first, x.second), v = std::min(x.first, y.second);
return u == v ? x.first > y.first : u > v;
});
i64 c = 0, s = 0;
for (int i = 0; i < n; ++i) {
s += pr[i].first;
c = std::max(c, s) + pr[i].second;
}
std::cout << c << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int T;
std::cin >> T;
while (T--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号