双端队列题解

Description

Sherry现在碰到了一个棘手的问题,有N个整数需要排序。 Sherry手头能用的工具就是若干个双端队列。 她需要依次处理这N个数,对于每个数,Sherry能做以下两件事:
1.新建一个双端队列,并将当前数作为这个队列中的唯一的数;
2.将当前数放入已有的队列的头之前或者尾之后。
对所有的数处理完成之后,Sherry将这些队列排序后就可以得到一个非降的序列。

Input

第一行包含一个整数N,表示整数的个数。接下来的1行包含N个整数Di,其中Di表示所需处理的整数。

Output

其中只包含一行,为Sherry最少需要的双端队列数。

Sample Input

6
3 6 0 9 6 3

Sample Output

2

Hint

N≤200000

分析

若将输入数组a
b为a将a排序后的数组
因为将队列排序后能得到单调序列,所以每一个队列为排序后都为排序后数组中连续一段
所以现在问题转化为求b中满足条件的最少划分段数

现在我们再来看怎样的划分是满足条件的
假设其中一个合法的为a[i],a[i+1]…a[j-1],a[j]
不难发现最终队列下标情况为i最小且i两边单调且单调性不同
大概是这样
在这里插入图片描述
就这样就行了

标程

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define SIZE 200005
using namespace std;

struct kkk
{
	int v,num;
	bool operator < (const kkk &other) const
	{
		if(v != other.v)
			return v < other.v;
		else
			return num < other.num;
	}
}a[SIZE];

int maxa[SIZE],mina[SIZE];

int main()
{
	freopen("deque.in","r",stdin);
	freopen("deque.out","w",stdout);
	memset(maxa,0xaf,sizeof(maxa));
	memset(mina,0x7f,sizeof(mina));
	int n;
	scanf("%d",&n);
	for(int i = 0;i < n;i++)
		scanf("%d",&a[i].v),a[i].num = i;
	sort(a,a+n);
	int nowa = a[0].v,nowk = 0;
	int tot = 0;
	for(int i = 0;i < n;i++)
	{
		if(a[i].v == nowa)
			a[i].v = nowk;
		else
		{
			nowa = a[i].v;
			nowk++;
			a[i].v = nowk;
		}
	}
	for(int i = 0;i < n;i++)
	{
		maxa[a[i].v] = max(maxa[a[i].v],a[i].num);
		mina[a[i].v] = min(mina[a[i].v],a[i].num);
	}
	if(nowk == 0)
	{
		cout<<1;
		return 0;
	}
	bool flag = true;
	int tot1 = 1,tot2 = 0;
	for(int i = 1;i <= nowk;i++)
	{
		if(flag)
		{
			if(mina[i] < maxa[i-1])
				flag = false;
		}
		else
		{
			if(maxa[i] > mina[i-1])
			{
				flag = true;
				tot1++;
			}
		}
	}
	if(!flag)
		tot1++;
	flag = false;
	for(int i = 1;i <= nowk;i++)
	{
		if(flag)
		{
			if(mina[i] < maxa[i-1])
				flag = false;
		}
		else
		{
			if(maxa[i] > mina[i-1])
			{
				flag = true;
				tot2++;
			}
		}
	}
	if(!flag)
		tot2++;
	cout<<min(tot2,tot1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值