相邻两项比较的方法深入人心(
感谢那个第一位写出此题正解且没有被 hack 掉的大佬。
建议这一道题目和国王游戏一起看
但是这两道题目在算法上却有一些本质的不同。(这一篇会主要讲讲)
整理做法
首先看到安排顺序,考虑进行相邻两项的比较,跟国王游戏的套路没啥区别;
再来看一眼题目中给出的表达式:
这其实就足以证明越往后钱就约多,然而皇后却希望奖金最多的大臣得到的钱要尽量少,所以我们要让后面的大臣的奖金尽量少;
相邻两项之间的比较
国王游戏这一道题目我们是比较的
说简单点也就是比较交换前的 \(\max(c_i,c_j)\) 和交换后的 \(\max(c_i,c_j)\) 但是这里我们显然是不用这么去比较的,其他题解都讲的很清楚力,由于 \(c_i\) 相对于其他的量来说是一只在变化的,所以,我们只需要比较 交换前的 \(c_j\) 和交换后的 \(c_i\) 就可以了;
然后其实这一部分比较就是一个一直化简、举例的过程,别的题解讲的很透了,这里不说了,总之我们珂以推出下面的柿子:
(注:这里满足的条件是 \(j=i+1\))
从这里我们可以得出要来:
若 \(\min(a_i,b_j) \le \max(a_j,b_i)\) 那么调换一下 \(i\) 和 \(j\) 的位置就可;
这个不受这两个大臣前后的大臣的影响。
但是为了证实这个是正确的,我们就需要考虑一个极端的可能性:
由于上面那个柿子和 \(a_i\) 与 \(b_i\) 的大小有着直接的关系,我们不妨分情况来看一下:
-
若 \(a_i \le b_i\),因此,\(a_j \le a_i\) 并且 \(a_j \le b_j\) 不难看出,这一组要按照 \(a\) 的升序 排列;
-
若 \(a_i>b_i\),则有 \(b_j \ge b_i\) 或者 \(b_j \ge a_j\) 这一组要按照 \(b\) 的降序 排列;
所以我们可以得出一个结论:
把 \(a \le b\) 的部分尽量放在前面,然后按照 \(a\) 的升序排列,后面的部分显然就是 \(a>b\) 的部分,按照 \(b\) 的降序排列
于是,可以得出代码:
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
struct Node {
ll a, b, t;
bool operator<(const Node& n2)const {
if (t != n2.t)
{
return t < n2.t;
}
if (t != 3) {
return a < n2.a;
}
else
{
return b > n2.b;
}
}
}node[100005];
//这里可以对比国王游戏的代码部分
int main() {
int T, n;
cin >> T;
while (T--)
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> node[i].a >> node[i].b;
if (node[i].a < node[i].b) {
//a[i]<b[i]的情况:
node[i].t = 1;
}
else if (node[i].a == node[i].b) {
//a[i]=b[i]的情况:
node[i].t = 2;
}
else {
node[i].t = 3;
}
}
sort(node + 1, node + 1 + n);
ll x, y;
x = node[1].a, y = node[1].a + node[1].b;//开始第一个人的钱数
for (int i = 2; i <= n; i++) {
x += node[i].a;
y = max(y, x) + node[i].b;
}
cout << y << endl;
}
return 0;
}
代码没啥好说的吧;
总结一下,这道题最难的地方是推柿子还有最后的证明过程,希望相邻两项比较的方法可以深入人心吧。