其实今天是第一次听说这个数据结构。顺便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; }
我们现在所用的办法外面搜索的时间是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); } }