POJ 3250 单调堆栈

其实今天是第一次听说这个数据结构。顺便AC了一道题目:

http://poj.org/problem?id=3250

先说说单调堆栈吧,其实网上对这个东西的介绍不是很具体,百度知道上也只是有很简单的描述,其实这个堆栈就是一种对压入堆栈的元素有一定限制的堆栈,例如单调递增或者单调递减。如果当前要压入堆栈的元素不满足递增或者递减要求,则从栈结构中弹出一些数据,直到变成空,或者满足单调条件。

所以这个堆栈显然会满足一些条件

1. 对于所有当前步骤压入堆栈的元素E,栈中的其余元素都是 递增(或递减)的大于(或小于)E

2. 对于弹出的所有元素,一定全都 小于或大于E

这个题目第一眼看到应该是一个 N^2 的暴力搜索,但是明显如果用暴力搜索的话一定会超时。

对于测试数据,我们可以做一下简单的分析,看看它为什么可以满足 单调堆栈 

10
3
7
4
12
2

        =
=       =
=   -   =         Cows facing right -->
=   =   =
= - = = =
= = = = = =
1 2 3 4 5 6 
在这里,我们用一个递减的单调堆栈来描述这个问题,当插入一个元素E的时候,要求所有的之前的元素都比E要大。

例如当前堆栈T,E1, E2, E3, E4.. En

当前要插入的元素是E

加入E<Ei, E>Ei+1

就是说我们要把Ei+1 ... En的元素都弹出来。

为什么这样做是合理的呢,观察可以发现,其实当有E出现在队列中的时候,Ei+1...En这些牛以后就再也看不见任何新的牛了,因为他们被E挡住了。而E1...Ei却都可以看到E的存在,这个时候E的加入会给整个牛队的【看到头的数目Sum】贡献. T弹出Ei+1...En后的size().


一份简单版本的代码如下:

#include <stdio.h>
#include <stack>

using namespace std;

int main()
{
	int nNums;
	long long nInputNum;
	while(scanf("%d",&nNums) != EOF)
	{
		stack<long long> t;
		long long sum = 0;
		while(nNums--)
		{
			scanf("%lld", &nInputNum);

			while(!t.empty() && t.top() <= nInputNum)
			{
				t.pop();
			}
			sum += t.size();
			t.push(nInputNum);
		}
		printf("%lld\n", sum);
	}


	return 0;
}

复杂度分析:如果用暴力搜索的话,复杂度是O(n^2)

我们现在所用的办法外面搜索的时间是n,而对于内部而言,每个元素都至少入栈一次,但是每个元素之多只能出栈一次。所用总的时间复杂度应该是 O(n+n) = O(n)


不过其实这个算法可以更快一点点,哪怕一点点........

其实我们整个堆栈里的元素都是从大到小排列的,而我们每次插入一个元素E的时候,目的都是为了找到那个【第一个比E小的元素】

有序数组,找到第一个比X小的数据下标,这是典型的binary search 问题的变种吧。


这样快一点点的最后结果是 n+log(n)

所以自己用队列实现一个单调堆栈,然后用binary search的方法找插入的合适位置,就能降低一点点复杂度了(从复杂度角度分析来讲是没有提高的)

代码如下(第一次尝试用 文本编辑器写程序)

#include <stdio.h>
#define MAX_COW 80005
long long stack[MAX_COW];
int nSize;

int FindFirstLessElementInArray(long long t)
{
	int nStartIdx, nEndIdx;
	nStartIdx = 0;
	nEndIdx = nSize - 1;
	int nMiddleIdx;
	while(nStartIdx <= nEndIdx)
	{
		nMiddleIdx = (nStartIdx + nEndIdx)/2;
		if(stack[nMiddleIdx] > t)
		{
			nStartIdx = nMiddleIdx + 1;
		}
		else
		{
			nEndIdx = nMiddleIdx - 1;
		}

	}
	return nStartIdx;
}
int main()
{
	int nCow;
	long long nHeight;
	while(scanf("%d", &nCow) != EOF)
	{
		nSize = 0;
		int nFirstLessEleIdx;
		long long sum = 0;
		for(int i = 0; i < nCow; i++)
		{
			scanf("%lld", &nHeight);
			nFirstLessEleIdx = FindFirstLessElementInArray(nHeight);
			stack[nFirstLessEleIdx] = nHeight;
			sum += nFirstLessEleIdx;
			nSize = nFirstLessEleIdx + 1;
		}
		printf("%lld\n", sum);		 
	}


}






你可能感兴趣的:(数据结构,算法,测试,百度,search,文本编辑)