第十二届蓝桥杯省赛 C++ B组 - 双向排序

✍个人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343
📚专栏地址:蓝桥杯题解集合
📝原题地址:双向排序
📣专栏定位:为想参加蓝桥杯的小伙伴整理常考算法题解,祝大家都能取得理想成绩!
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

问题描述

在这里插入图片描述

思路

这道题对于我这种新手小白也挺难的,看 AcWing 看了好久才弄明白,话不多说,直接上解析!
首先我们要考虑下面的几种情况:
(1)操作一(q 为 0 降序)
当出现连续的操作一时,我们只需找到其中跨越区间最大的那一个即可,因为小于它的其它区间都包含在最长的这个区间里,就不用重复的去完成降序操作了。
在这里插入图片描述
(2)操作二(q 为 1 升序)
这个操作必须在前面进行了操作一的前提下才能进行,因为如果前面没有操作一,直接进行操作二没有意义,原本的序列就是升序的。
同样,如果出现连续的情况,要找到区间最长的那个操作。
在这里插入图片描述
(3)操作一和操作二交替进行
如果操作一和操作二不是连续的,并且你当前的操作的区间要大于之前的操作一或者操作二,那么就要将当前操作的前面两个操作删除,因为当前操作会覆盖掉之前的操作。例如,当前操作一比上次操作一的区间要大,那我就将上次操作一给删除,但是两次操作二就会相邻,所以就要将操作一和操作二一同删除。
在这里插入图片描述

经过上面三种情况的排除后,我们可以发现一个规律,我的所有操作中,我的第一次操作一和操作二一定时所有操作一和操作二中包含区间最大的,因为比他小的之前的区间或相邻的区间都删除了,所以该操作之后的不相邻操作区间会不断的减小,并且是操作一和操作二交错进行的。
这样,我们只用改变相邻操作一和操作二重叠部分的次序即可,通过不断地操作,中间公共区域会变得越来越小即左边界和右边界会逐渐往中间去靠,直至左右边界相遇。
所以,我们每次操作都会改变左边界和右边界,而左边界的左边和右边界的右边就是已经排好序的部分,通过不断的缩小中间区域来逐渐填满整个列表。
在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;

const int N = 100010;
PII stk[N];
int ans[N];
int m, n;

int main()
{
	scanf("%d%d", &n, &m);
	int top = 0;
	while (m--)
	{
		int p, q;
		scanf("%d%d", &p, &q);
		//如果是操作一
		if (!p)
		{
			//如果连续执行操作一,则只保留右边界值最大的那个
			while (top && stk[top].x == 0)	q = max(q, stk[top--].y);
			//如果当前的操作右边界要比上一次操作一的右边界值大,则删除前面的操作一和操作二
			while (top >= 2 && stk[top - 1].y <= q)	top -= 2;
			stk[++top] = { 0,q };	//保存本次最佳的值
		}
		else if (top)	//操作二之前必须要有操作一,不然没有意义
		{
			//如果连续执行操作二,则只保留左边界值最小的那个
			while (top && stk[top].x == 1)	q = min(q, stk[top--].y);
			//如果当前的操作左边界要比上一次操作二的左边界值小,则删除前面的操作一和操作二
			while (top >= 2 && stk[top - 1].y >= q)	top -= 2;
			stk[++top] = { 1,q };	//保存本次最佳的值
		}
	}

	int k = n, l = 1, r = n;
	for (int i = 1; i <= top; i++)
	{
		if (stk[i].x == 0)	//如果是操作一
			while (r > stk[i].y && l <= r)	ans[r--] = k--;	//右边界左移,并赋值(升序)
		else
			while (l < stk[i].y && l <= r)	ans[l++] = k--;	//左边界右移,并赋值(降序)
		if (l > r)	break;
	}

	//如果 l 和 r 没有相遇即中间仍有空的区域,则需要根据最后一次操作填满
	if (top % 2 == 0)	//如果最后一次操为作一,则需要将中间按降序填满
		while (l <= r)	ans[r--] = k--;
	else    //如果最后一次为操作二,则需要将中间按升序填满
		while (l <= r)	ans[l++] = k--;

	for (int i = 1; i <= n; i++)
		printf("%d ", ans[i]);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值