洛谷 P2587 题解
题意简述
A、B 两队各有 \(n\) 匹马。A 队的马的能力值为 \(a_1, a_2, \cdots, a_n\),B 队的马的能力值为 \(b_1, b_2, \cdots, b_n\)。两队之间要进行 \(n\) 场一对一的赛马比赛,每场比赛中胜的队伍得 \(2\) 分,平的队伍得 \(1\) 分,负的队伍不得分。两队的出马顺序可以任意排列,求 A 队能获得的最高分和最低分。
思路
注意到两队的总得分和为 \(2n\)(每场比赛中分出胜负为 \(2 + 0 = 2\),平为 \(1 + 1 = 2\)),所以求 A 队的最低分可以转化为求 B 队的最高分。
贪心策略(以求 A 队最高分为例)
在所有未被配对的马中,设 A 队最弱和最强的马的能力值分别是 \(a_i\) 和 \(a_k\),B 队最弱和最强的马的能力值分别是 \(b_j\) 和 \(b_l\)。分类讨论:
- 若 \(a_i > b_j\),则配对 \((a_i, b_j)\),A 队胜;
- 若 \(a_i < b_j\),则配对 \((a_i, b_l)\),A 队负;
- 若 \(a_i = b_j\):
- 若 \(a_k > b_l\),则配对 \((a_k, b_l)\),A 队胜;
- 否则配对 \((a_i, b_l)\),A 队负或平。
证明
为什么 \(a_i > b_j\) 时可以直接配对 \((a_i, b_j)\) 而不把 \(a_i\) 当成以后牺牲掉的弱马?考虑任意一组他解 \(S'\)——配对 \((a_i, b_y), \ (a_x, b_j)\)。我们希望证明:原解 \(S\)——配对 \((a_i, b_j), \ (a_x, b_y)\) 与之相比是不劣的。
由 \(i, j\) 定义知 \(b_j < a_i \le a_x, \ b_j \le b_y\)。对 \(b_y\) 分类讨论:
- 若 \(b_y < a_x\),则 \(S\) 中 A 队 2 胜(\(a_i\) 胜 \(b_j\),\(a_x\) 胜 \(b_y\)),\(S\) 一定不劣;
- 若 \(b_j < a_i \le a_x = b_y\),则 \(S\) 中 A 队 1 胜 1 平(\(a_i\) 胜 \(b_j\),\(a_x\) 平 \(b_y\)),\(S'\) 中 A 队 1 胜 1 平或 1 胜 1 负(\(a_x\) 胜 \(b_j\),\(a_i\) 与 \(b_y\) 负或平),\(S\) 不劣;
- 若 \(b_j < a_i \le a_x < b_y\),则 \(S\) 中 A 队 1 胜 1 负(\(a_i\) 胜 \(b_j\),\(a_x\) 负 \(b_y\)),\(S'\) 中 A 队 1 胜 1 负(\(a_x\) 胜 \(b_j\),\(a_i\) 负 \(b_y\)),\(S\) 不劣。
所以 \(S\) 一定不劣,命题得证。
若 \(a_i < b_j\),则用 \(a_i\) 换掉最强的 \(b_l\) 是显然正确的。因为 \(a_i\) 和谁比都是负,并且换掉 B 队中任何一只弱于 \(b_l\) 的马都不可能更优。
若 \(a_i = b_j, \ a_k \le b_l\),我们证明:配对 \((a_i, b_l)\) 一定最优。类似地,考虑任意一组他解 \(S'\)——配对 \((a_i, b_y), \ (a_x, b_l)\)。我们希望证明:原解 \(S\)——配对 \((a_i, b_l), \ (a_x, b_y)\) 与之相比是不劣的。
由 \(i, j, l\) 定义知 \(a_i \le a_x, b_y \le b_l\)。此时分类讨论过于繁杂,不如列式放缩。我们设 \((a_i, b_l), \ (a_x, b_y), \ (a_i, b_y), \ (a_x, b_l)\) 四场比赛中 A 队的得分分别为 \(s_1, s_2, s_3, s_4\),则命题转化为求证:\(s_1 + s_2 \ge s_3 + s_4\)。有以下几点性质:
- 因为 \(b_l \ge b_y\),所以 \(s_2 \ge s_4\);
- 因为 \(a_i \le b_l\),所以 \(0 \le s_1 \le 1\);
- 因为 \(a_i \le b_y\),所以 \(0 \le s_3 \le 1\)。
则有
如果要取到 \(-1\),那么两个 \(\ge\) 都要取到等号。第一个等号成立当且仅当 \(b_y = b_l\) 或 \(a_x < b_y\),第二个等号成立当且仅当 \(a_i < b_l\) 且 \(a_i = b_y\)。两者不可能同时成立。因此 \(\Delta \ge 0\),\(S\) 一定不劣,命题得证。
实现
将 \(a, b\) 升序排列,用四个指针维护 \(i, j, k, l\),按照贪心策略求解即可。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5;
int a[N + 5], b[N + 5];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, ans1 = 0, ans2 = 0;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
for (int cnt = 1, i = 1, j = 1, k = n, l = n; cnt <= n; cnt++)
if (a[i] > b[j])
{
ans1 += 2;
i++, j++;
}
else if (a[i] < b[j])
i++, l--;
else if (a[i] == b[j])
if (a[k] > b[l])
{
ans1 += 2;
k--, l--;
}
else
{
ans1 += (a[i] == b[l]);
i++, l--;
}
for (int cnt = 1, i = 1, j = 1, k = n, l = n; cnt <= n; cnt++)
if (b[i] > a[j])
{
ans2 += 2;
i++, j++;
}
else if (b[i] < a[j])
i++, l--;
else if (b[i] == a[j])
if (b[k] > a[l])
{
ans2 += 2;
k--, l--;
}
else
{
ans2 += (b[i] == a[l]);
i++, l--;
}
cout << ans1 << " " << 2 * n - ans2 << "\n";
return 0;
}
本文来自博客园,作者:lzy20091001,转载请注明原文链接:https://www.cnblogs.com/lzy20091001/p/18993971

浙公网安备 33010602011771号