小Q的最小值序列

1.【题目描述】

  小 Q 得到了一个长度为 n 的序列 A,A 中的数各不相同。对于 A 中的每一个
数 Ai,求:min(1≤j<i)|Ai−Aj|
  以及令上式取到最小值的 j(记为 P_i)。若最小值点不唯一,则选择使 Aj 较小的那个。
输入描述:
第一行一个整数 n,第二行 n 个数。
输出描述: n-1 行,每行 2 个用空格隔开的整数。分别表示当 i 取 2~n 时,对应的 min(1≤j<i)|Ai−Aj|和 Pi的值
输入样例:
3
1 5 3
输出样例:
4 1
2 1

2.【问题分析】

1.暴力破解的代价太大。进一步考虑,如果输入序列有序,那么每次检索比较的范围可以缩小到三个数之间
2.问题要求输出相应Pi的值,则选用数组等数据结构不可取,需要借助用户自定义类型,这里我们选择双向链表,由于初始化(即插入)工作一次完成,后边不做修改,可以将其简化为静态双向链表
3.需要借助辅助数组来维护原始序列的元素位置和下标。

3.【图示】

在这里插入图片描述

4.【代码】
#include<bits/stdc++.h>

using namespace std;
/*
小Q的最小值序列

3
1 5 3
*/
const int N = 1e5 + 5;    //开辟数组的空间大小
const int inf = 1e9;

struct P
{
	int value;
	int pos;
};
P a[N];

int cmp(P x, P y)    
{
	return x.value < y.value;
}

int b[N], b2[N], cnt, head, tail, ans1[N], ans2[N];
struct node
{
	int value;
	int next, pre;
}e[N];

void init()    //初始化一个静态的双向链表
{
	cnt = 2;
	head = 1;
	tail = 2;
	e[head].next = tail;
	e[tail].pre = head;
}

void ins(int pos, int x)    //相当于双向链表的插入操作
{
	e[++cnt].value = x;
	e[cnt].next = e[pos].next;
	e[cnt].pre = pos;
	e[e[pos].next].pre = cnt;
	e[pos].next = cnt;
}

void del(int pos)    //相当于双向链表的删除操作
{
	e[e[pos].next].pre = e[pos].pre;
	e[e[pos].pre].next = e[pos].next;
}

int main()
{
	int n;    //元素的总数
	scanf_s("%d", &n);

	for (int i = 1; i <= n; ++i)    //初始化输入序列
	{
		scanf_s("%d", &a[i].value);
		a[i].pos = i;
	}
	sort(a+1, a+1+n, cmp);    //经过排序操作,下标将会打乱次序

	init();
	for (int i = 1; i <= n; ++i)
	{
		ins(e[tail].pre, a[i].value);    //尾插
		b[a[i].pos] = cnt;    //记录元素在e中的的位置,将a[i]中的元素插入e中,并还原其初始位置,与排序前位置保持一致
		b2[cnt] = a[i].pos;    //维护b[i]的下标
	}

	for (int i = n; i >= 2; --i)
	{
		ans1[i] = inf;
		if (e[b[i]].next != tail)    //从最后一个元素开始
		{
			ans1[i] = min(ans1[i], abs(e[b[i]].value - e[e[b[i]].next].value));
			ans2[i] = b2[e[b[i]].next];
		}

		if (e[b[i]].pre != head)    //从最后一个元素开始
		{
			if (ans1[i] >= abs(e[b[i]].value - e[e[b[i]].pre].value))
			{
				ans1[i] = abs(e[b[i]].value - e[e[b[i]].pre].value);
				ans2[i] = b2[e[b[i]].pre];
			}
			
		}
		del(b[i]);    //极其重要
	}

	for (int i = 2; i <= n; ++i)
	{
		printf("%d %d\n", ans1[i], ans2[i]);
	}
	return 0;
}
4.【运行结果】

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值