题解 C. Restricted Sorting
题目位置: https://codeforces.com/contest/2188/problem/C.
现在我要认真解决每一道题目,要相信一定会有提高的.
题目描述
你有一个长度为 N N N 的序列 A A A, 现在你需要将这个序列从小到大排序.
你有一个操作:
- 交换 A A A 数组中的两个数字,我们设交换的位置为 i , j i,j i,j, 那么你需要满足 k ≤ ∣ A i − A j ∣ k \leq |A_i-A_j| k≤∣Ai−Aj∣.
现在要求出最大的 k k k.
思路
这个真的太巧妙了!
我们设这个序列的最小值为 P P P, 最大值为 Q Q Q.
首先我们把这个数组排序,得到序列 B B B.
当 A i = B i A_i=B_i Ai=Bi 时,这个位置是不需要交换的, 因为已经到达自己的位置了.
当 A i ≠ B i A_i\neq B_i Ai=Bi 时,这个位置需要交换.
如果这个序列是递增的,也就是这个序列本身就排好序了,那么我们直接输出 − 1 -1 −1 即可.
否则, 我们来考虑什么样子的 k k k 可以满足这种条件.
- 如果 Q − P < k Q-P<k Q−P<k, 那么也不会满足条件.
- 原因很简单,我们发现这个序列不是升序的,那么一定要交换两个元素,观察 k ≤ ∣ A i − A j ∣ k \leq |A_i-A_j| k≤∣Ai−Aj∣, 肯定是 ∣ A i − A j ∣ |A_i-A_j| ∣Ai−Aj∣ 越大越好.
- 什么时候 ∣ A i − A j ∣ |A_i-A_j| ∣Ai−Aj∣ 最大? 答案就是 Q − P Q-P Q−P 时候.
- 如果在最大的情况下都无法交换,那么其他无序的也一定不能交换
- 如果 A i ≠ B i A_i\neq B_i Ai=Bi 且 A i − P < k A_i-P<k Ai−P<k 且 Q − A i < k Q-A_i<k Q−Ai<k, 就代表着这个节点无法交换,所以 k k k 也不合法.
- 其他的情况那么 k k k 就是合法的.
我们继续考虑合法的情况.
这里设 j j j 为 i i i 应该到达的位置.
如果 A i − P ≥ k A_i-P\geq k Ai−P≥k 且 A j − P ≥ k A_j-P\geq k Aj−P≥k.
这就代表着我们可以借助最小值,将 A i A_i Ai 换到 A j A_j Aj.
这个序列就是 { A i , A j , P } \{A_i,A_j,P\} {Ai,Aj,P}.
现在我们要将 A j A_j Aj 和 A i A_i Ai 交换
- 交换 A i A_i Ai 和 P P P, 得 { P , A j , A i } \{P,A_j,A_i\} {P,Aj,Ai}.
- 交换 A j A_j Aj 和 P P P, 得 { A j , P , A i } \{A_j,P,A_i\} {Aj,P,Ai}.
- 交换 A i A_i Ai 和 P P P, 得 { A j , A i , P } \{A_j,A_i,P\} {Aj,Ai,P}.
在实际的代码中并不需要写出这些东西,只需要知道贡献即可.
贡献的推导.
上面的过程是我们知道 k 时如何最优的反转 , 那么我们现在在不知道 k 的情况下怎么操作?.
我们现在要让 k k k 最大,那么可以知道 k = min ( A i − P , A j − P ) k=\min(A_i-P,A_j-P) k=min(Ai−P,Aj−P).
如果 A i − P ≥ k A_i-P\geq k Ai−P≥k 且 Q − A j ≥ k Q-A_j\geq k Q−Aj≥k 时.那么我们就是要同时借助最大和最小值进行移位.
这个序列就是 { A i , A j , P , Q } \{A_i,A_j,P,Q\} {Ai,Aj,P,Q}.
- 交换 A i A_i Ai 和 P P P, { P , A j , A i , Q } \{P,A_j,A_i,Q\} {P,Aj,Ai,Q}.
- 交换 Q Q Q 和 P P P, { Q , A j , A i , P } \{Q,A_j,A_i,P\} {Q,Aj,Ai,P}.
- 交换 A j A_j Aj 和 Q Q Q, { A j , Q , A i , P } \{A_j,Q,A_i,P\} {Aj,Q,Ai,P}.
- 交换 Q Q Q 和 P P P, { A j , P , A i , Q } \{A_j,P,A_i,Q\} {Aj,P,Ai,Q}.
- 交换 P P P 和 A i A_i Ai, { A j , A i , P , Q } \{A_j, A_i, P, Q\} {Aj,Ai,P,Q}.
这里的贡献就是 k = min ( A i − P , Q − A j ) k=\min(A_i-P, Q-A_j) k=min(Ai−P,Q−Aj).
我们肯定要让 k k k 最大,因为 ∣ A i − A j ∣ ≥ k |A_i-A_j|\geq k ∣Ai−Aj∣≥k, 所以我们要最大化每一个点的贡献.
那么一个点的贡献就是: max ( min ( A i − P , A j − P ) , min ( A i − P , Q − A j ) ) \max(\min(A_i-P,A_j-P), \min(A_i-P, Q-A_j)) max(min(Ai−P,Aj−P),min(Ai−P,Q−Aj)).
所有点的贡献就是:
a n s = min i = 1 N ( max ( min ( A i − P , A j − P ) , min ( A i − P , Q − A j ) ) ) ans=\min_{i=1}^{N}(\max(\min(A_i-P,A_j-P), \min(A_i-P, Q-A_j))) ans=i=1minN(max(min(Ai−P,Aj−P),min(Ai−P,Q−Aj)))
直接计算显然是 O ( N 2 ) O(N^2) O(N2) 的,因为要枚举每一个点及其对应点.
发现就是每一个点取 max \max max, 然后再取 min \min min.
所以里面的两个 min \min min 可以作为重复部分去掉.
所以答案为 a n s = min i = 1 N ( max ( A i − P , Q − A i ) ) ) ans=\min_{i=1}^{N}(\max(A_i-P,Q-A_i))) ans=mini=1N(max(Ai−P,Q−Ai))).
按照这个计算即可,时间复杂度为 O ( N ) O(N) O(N).
代码
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
void solve();
int main()
{
// freopen("Sample.in", "r", stdin);
int T;
scanf("%d", &T);
while (T--) solve();
return 0;
}
#define MAXN 200010
int N;
void solve() {
scanf("%d", &N);
vector<int> A(N);
for (int& it : A) scanf("%d", &it);
vector<int> B = A;
sort(B.begin(), B.end());
if (A == B) return printf("-1\n"), void();
int ans = 2e9;
for (int i = 0; i < N; i++) {
if (A[i] == B[i]) continue;
ans = min(ans, max(B[i] - B[0], B[N - 1] - B[i]));
}
printf("%d\n", ans);
}