奇怪的排序题解

来自bzoj.org/p/P06331(传统题)

题面:


给你一个数字N,再给你一个N的全排列。

希望你对这个全排列按权值大小进行升序排列,但给你可用的工具却非常奇葩。

给一个长条桌子,上面有若干个空位置,在这些空位置上,你可以新建立一个柱子出来

希望你对这个全排列从左扫到右,对于扫到的第i个数字ai

你可以进行以下两种选择之一

1:将ai放到某个已建好的柱子的顶部

2:在桌子上从左到右,找到一个空位置,并建立一个新柱子,将ai放在柱子上。

当然在放ai前,你也可以在一个非空的柱子上,取出其最上面的元素,将其丢到结果数组中

并希望结果数组中所放的数字,其值是升序排列的,且序列长度越长越好

基本问题:


这道题和'空当接龙游戏(bzoj.org/p/Z2149)'很类似,同样是要用到单调栈。

由于这道题要用到多个栈,所以傻傻的(bushi)开那么多栈是不行的,不然会炸的很爽...

其实,我们可以通过以下几个数组来实现n个栈:

top[x] = 第x个栈的头元素。

under[x] = 数字x下面的元素。

last[x] = 第x个栈的尾元素。(本题特殊需要)

sum = 栈的数量。

也就是说,用这几个数组,能模拟出一个“栈群”,从而初步解决问题。


核心思路:


无论如何开栈,最后的读数永远是从左往右,从顶到底的弹出。

所以,我们的单调栈必须是严格上升的,为了保证最优解,每一个数要放在栈顶元素大于这个数且栈顶元素尽量小的栈里

int x;
cin >> x;
if(x > top[sum])
{
	sum++;
	top[sum] = x;
	tot[sum] = 1;
}
else
{
	int t = upper_bound(top + 1,top + 1 + n,x) - top;
	under[x] = top[t];
	top[t] = x;
	tot[t]++;
}

但是,题目不止这么简单,题目中说的取出栈顶元素非常的重要


特殊案例:

例如4 5 2 3 1这个序列,它运行起来是怎样的呢?

4 单独开栈。

5 单独开栈。

2 加入1号栈,1号栈变为{4,2}。

3加入2号栈,2号栈变为{5,3}。

1加入1号栈,1号栈变为{1,4,2}。

这时,1号栈出栈后,出栈序列变为1,2,4,这时候,3和5就出不了栈了

这时,提前出栈就派上了大用场。

如果在3进栈前,把2出栈,再把3加入1号栈,这样虽然会导致1无法出栈,但出栈序列可以延长至2,3,4,5。

很明显,我们要把upper_bould中的top换成last,当上文中的top[t]小于x时,不断弹出栈顶元素,并记录下最大值,若后续某个数小于这个最大值,则停止进栈并输出结果。

if(last[sum] < x) 
{
	sum++;
	last[sum] = top[sum] = x;
	continue;
}
else 
{
	int p = upper_bound(last + 1,last + 1 + sum,x) - last;
	int t = top[p];
	while(t && t < x)
	{
		maxn = max(maxn,t);
		t = under[t];
	}
	if(!t) last[p] = top[p] = x;
	else
	{
		under[x] = t;
		top[p] = x;
	}
}

 最后,如果没有出现特殊情况,答案就是n。


参考代码:

#include <bits/stdc++.h>
using namespace std; 
int n,sum;
int top[100005],last[100005],under[100005];
int main()
{
	cin >> n;
	int maxn = 0;
	for(int i = 1;i <= n;i ++) 
	{
		int x;
		cin >> x;
		if(x < maxn) 
		{
			cout << i - 1;
			return 0;
		}
		if(last[sum] < x) 
		{
			sum++;
			last[sum] = top[sum] = x;
			continue;
		}
		else 
		{
			int p = upper_bound(last + 1,last + 1 + sum,x) - last;
			int t = top[p];
			while(t && t < x)
			{
				maxn = max(maxn,t);
				t = under[t];
			}
			if(!t) last[p] = top[p] = x;
			else
			{
				under[x] = t;
				top[p] = x;
			}
		}
	}
	cout << n;
}

案例运行结果:

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
快速排序是一种常用的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后再按此方法对这两部分记录分别进行快速排序,以达到整个序列有序的目的。 以下是JAVA中快速排序的实现代码: ```java public class QuickSort { public static void sort(int[] arr, int start, int end) { if (start < end) { int pivot = partition(arr, start, end); sort(arr, start, pivot - 1); sort(arr, pivot + 1, end); } } private static int partition(int[] arr, int start, int end) { int pivot = arr[start]; while (start < end) { while (start < end && arr[end] >= pivot) { end--; } arr[start] = arr[end]; while (start < end && arr[start] <= pivot) { start++; } arr[end] = arr[start]; } arr[start] = pivot; return start; } public static void main(String[] args) { int[] arr = {5, 6, 8, 1, 3, 2, 9, 4, 7}; sort(arr, 0, arr.length - 1); for (int i : arr) { System.out.print(i + " "); } } } ``` 在快速排序中,需要选择一个元素作为枢轴(pivot),这里选择第一个元素作为枢轴。然后将序列中的元素分为两部分,一部分比枢轴小,一部分比枢轴大。然后再对这两部分分别进行快速排序,最终得到有序序列。 在上述代码中,partition方法是关键,它实现了分割序列的功能。首先将枢轴存储在pivot变量中,然后从序列的两端开始遍历,找到第一个比枢轴小的元素和第一个比枢轴大的元素,然后将它们交换位置。重复这个过程直到start和end重合,最后将枢轴放到这个位置上。 sort方法是递归实现的,每次选择一个枢轴,然后将序列分为两部分,分别对这两部分进行快速排序,直到序列有序为止。 最后在main方法中,我们对一个无序序列进行快速排序,并输出有序序列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值