邻值查找(0x13 链表与邻接表)

邻值查找

题意

给定一个长度为 n n n 的序列 A A A A A A 中的数各不相同。

对于 A A A 中的每一个数 A i A_i Ai,求:

m i n 1 ≤ j < i ∣ A i − A j ∣ min_{1≤j<i}|A_i−A_j| min1j<iAiAj
以及令上式取到最小值的 j j j(记为 P i P_i Pi)。若最小值点不唯一,则选择使 A j A_j Aj 较小的那个。

思路

将原数组按照权值递增排序后,构造成一个链表,然后按照输入顺序从后往前计算结果,每算出一个数 A i A_i Ai的结果后,就将其从链表中删除,这样可以保证计算第 i 个数时,所有还存在的数在输入中一定在其前面 满足了题目要求的 1<=j<i

实现细节请看代码注释

代码
#include<bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }

const int N = 1e5 + 10;

int n;
PII a[N]; // 记录每个数的权值和输入的下标
PII res[N]; // 记录答案
int p[N]; // 记录每个数排序后的下标
int l[N], r[N]; // 前向指针和后向指针数组

void solve() {
	cin >> n;
	rep(i, 1, n) {
		cin >> a[i].first; // 读入权值
		a[i].second = i; // 记录下标
	}

	sort(a + 1, a + 1 + n); // 按权值递增排序
	rep(i, 1, n) {
		l[i] = i - 1; // 对排序后的数据构造链表
		r[i] = i + 1;
		p[a[i].second] = i; // 记录排序前的下标对应的排序后的下标
	}

	a[0].first = a[n + 1].first = LLONG_MAX; // 边界

	for (int i = n; i > 1; --i) { // 按输入顺序从后往前计算答案
		int j = p[i]; // j 表示第 i 个输入的数排序后在链表中的位置(下标)
		int left = l[j], right = r[j]; // 当前数在链表中的前驱节点和后继节点的下标
		int v = a[j].first; // 当前节点的权值
		int lv = abs(a[left].first - v); // 与左边节点的差的绝对值
		int rv = abs(a[right].first - v); // 与右边节点的差的绝对值

		if (lv <= rv) { // 如果当前数与 左边的数的差值 小于 与右边的数的插值 按照题目要求选择左边的数
			res[i] = { lv,a[left].second };
			// a[left].second 表示左边的数在原数组中的下标
		}
		else { // 否则选择右边的点
			res[i] = { rv,a[right].second };
		}

		// 将当前节点从链表中删除
		l[right] = left;
		r[left] = right;
	}

	// 按输入顺序输出答案
	rep(i, 2, n) {
		printf("%lld %lld\n", res[i].first, res[i].second);
	}
}

signed main() {
	// int t; cin >> t;
	// while (t--)
		solve();

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzqwtc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值