题目地址:
https://www.acwing.com/problem/content/138/
给定一个长度为 n n n的序列 A A A, A A A中的数各不相同。对于 A A A中的每一个数 A i A_i Ai,求: min 1 ≤ j < i ∣ A i − A j ∣ \min_{1≤j<i}|A_i−A_j| 1≤j<imin∣Ai−Aj∣以及令上式取到最小值的 j j j(记为 P i P_i Pi)。若最小值点不唯一,则选择使 A j A_j Aj较小的那个。
输入格式:
第一行输入整数
n
n
n,代表序列长度。
第二行输入
n
n
n个整数
A
1
…
A
n
A_1…A_n
A1…An,代表序列的具体数值,数值之间用空格隔开。
输出格式:
输出共
n
−
1
n−1
n−1行,每行输出两个整数,数值之间用空格隔开。分别表示当
i
i
i取
2
∼
n
2∼n
2∼n时,对应的
min
1
≤
j
<
i
∣
A
i
−
A
j
∣
\min_{1≤j<i}|A_i−A_j|
min1≤j<i∣Ai−Aj∣和
P
i
P_i
Pi的值。
数据范围:
n
≤
1
0
5
,
∣
A
i
∣
≤
1
0
9
n≤10^5,|A_i|≤10^9
n≤105,∣Ai∣≤109
方法有很多,比如平衡树,单调栈,等等。这里用链表做。先将所有数做成一个pair,即 ( A [ i ] , i ) (A[i],i) (A[i],i),对其排序,然后将这些数从左到右依次插进一个链表里,同时要记录一下每个数在链表中的节点位置 p [ i ] p[i] p[i],即 p [ i ] p[i] p[i]是 A [ i ] A[i] A[i]对应的链表节点。接着 i i i从 n → 2 n\to 2 n→2遍历,先找到 A [ i ] A[i] A[i]的位置 p [ i ] p[i] p[i],找到其前驱和后继,那么比它小的最大数和比它大的最小数就找到了(这里为了方便判断,链表的头尾各加一个哨兵,左边的哨兵赋值为 1 0 9 10^9 109,右边赋值为 − 1 0 9 -10^9 −109,这样不需要特判 p [ i ] p[i] p[i]恰好在首尾的情况,哨兵的取值要使得哨兵不会存在于答案),找到了之后比较一下哪个更近,一样近则按照题目要求取值较小的,得到 A [ i ] A[i] A[i]的答案之后将其在链表中删去,然后继续求 A [ i − 1 ] A[i-1] A[i−1]的答案。代码如下:
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
using PII = pair<int, int>;
// 数据里有1e9,INF要多取1个
const int N = 1e5 + 10, INF = 1e9 + 1;
int n;
int p[N], l[N], r[N];
PII a[N], res[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].x);
a[i].y = i;
}
sort(a + 1, a + 1 + n);
a[0].x = INF, a[n + 1].x = -INF;
r[0] = 1, l[n + 1] = n;
for (int i = 1; i <= n; i++) {
l[i] = i - 1, r[i] = i + 1;
p[a[i].y] = i;
}
for (int i = n; i > 1; i--) {
int pos = p[i], left = l[pos], right = r[pos];
int lv = abs(a[pos].x - a[left].x), rv = abs(a[pos].x - a[right].x);
if (lv < rv) res[i] = {lv, a[left].y};
else if (lv > rv) res[i] = {rv, a[right].y};
else res[i] = {lv, a[left].x < a[right].x ? a[left].y : a[right].y};
r[left] = right, l[right] = left;
}
for (int i = 2; i <= n; i++) printf("%d %d\n", res[i].x, res[i].y);
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。