其实今天是第一次听说这个数据结构。顺便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);
}
}