题解:P14298 [JOI2023 预选赛 R2] JOI 运动会 / JOI04

· · 题解

题目大意:

传送门

我们要从 a,b,c,d 数组中各选一个数,使得这 4 个数的最大值与最小值的差最小。

这相当于找到一个最紧凑的四元组。

题目思路:

前提双指针

如果我们确定了 4 个数的最小值,那么为了让差值最小:

首先,先排序。

这时候我们就需要用到四个指针 [i,j,k,l],来表示 4 个数组的下标。

那么显然,目前的答案就是:

int maxn = max({a[i], b[j], c[k], d[l]});//最大值
int minn = min({a[i], b[j], c[k], d[l]});//最小值
mi = min(mi, maxn - minn);//最大值与最小值的差,就是答案

那我们又要如何更新指针呢?

我们这边假设(下标从 1 开始):

则目前的 \operatorname{maxn}=10,\operatorname{minn}=1, \operatorname{mi}=\operatorname{maxn}-\operatorname{minn}=10-1=9

现在让 l+1,得:

我们发现,$\operatorname{mi}$ 反而更大了,若现在让 $i+1$,得: $\operatorname{maxn}=10,\operatorname{minn}=2, \operatorname{mi}=\operatorname{maxn}-\operatorname{minn}=10-2=8$。 我们发现,$\operatorname{mi}$ 变小了,答案更优了,说明每次更新最小值的指针即可。 ::::success[证明] - 若更新最小值,$\operatorname{mi}$ 显然会变小。 - 若更新最大值,$\operatorname{mi}$ 显然会变大。 - 若更新中间值,$\operatorname{mi}$ 显然不变,没有任何效果。 故,更新最小值最优。 :::: 例如: ```cpp if (a[i] == minn) ++i; else if (b[j] == minn) ++j; else if (c[k] == minn) ++k; else ++l; ``` 最后,输出 $\operatorname{mi}$ 即可。 最后梳理: - 排序:将四个班级的身高数组分别排序。 - 四指针扫描:使用四个指针分别指向四个数组的当前位置。 - 贪心策略:每次移动当前最小值所在的指针。 - 更新答案:记录过程中的最小差值。 ## 代码: ```cpp #include<bits/stdc++.h> using namespace std; #define int long long const int N = 75010; int n, a[N], b[N], c[N], d[N], mi = 1e9; signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n; int i; for (i = 1; i <= n; i++) cin >> a[i]; for (i = 1; i <= n; i++) cin >> b[i]; for (i = 1; i <= n; i++) cin >> c[i]; for (i = 1; i <= n; i++) cin >> d[i]; sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + n); sort(c + 1, c + 1 + n); sort(d + 1, d + 1 + n); i = 1; int j = 1, k = 1, l = 1; while (i <= n && j <= n && k <= n && l <= n) { int maxn = max({a[i], b[j], c[k], d[l]}); int minn = min({a[i], b[j], c[k], d[l]}); mi = min(mi, maxn - minn); if (a[i] == minn) ++i; else if (b[j] == minn) ++j; else if (c[k] == minn) ++k; else ++l; } cout << mi; CODE BY no_coding_extra return 0; } ```