![](https://i-blog.csdnimg.cn/blog_migrate/aa015e49e636323e3fd61cb14f95e351.jpeg)
农夫John 的N(1 ≤ N ≤ 80,000)只奶牛中,有一些也许正在经历发型糟糕的一天。每只奶牛对自己乱糟糟的发型都有自知之明,农夫John想知道所有奶牛能看到其他奶牛头顶的数量之和。
任意奶牛i身高记为 hi (1 ≤ hi ≤ 1,000,000,000),所有奶牛面向东方(本题示意图的右面)依次站成一条线。因此,奶牛i能够看到在它前面的(奶牛i+1,i+2…)所有身高比它低的奶牛,直到被一头比它高的奶牛挡住
考虑如下的例子:
= = = = - = Cows facing right -> = = = = - = = = = = = = = = 1 2 3 4 5 6
奶牛#1 可以看见奶牛#2, 3, 4的头顶
奶牛#2 无法看到任何奶牛的头顶
奶牛#3可以看见奶牛#4的头顶
奶牛#4无法看到任何奶牛的头顶
奶牛#5可以看见奶牛#6的头顶
奶牛#6无法看到任何奶牛的头顶!
用ci表示奶牛i能够看到头顶的奶牛个数;请计算c1 至cN的和。对于上面这个例子,其和为:3 + 0 + 1 + 0 + 1 + 0 = 5。
输入 第1行:奶牛数N
第2行至N+1行:第i+1行包含一个整数,表示奶牛i的高度
第1行:c1 至cN的累加和
分析:
这个题中实际上也是利用了单调队列。单调队列的思路就是:保证在我们创建的队列中元素是有序的(在这个题里是降序的)。有的人可能会问,既然有了标准的priority_queue,我们为什么还要自己去实现,因为在我们的自己的实现的队列中可以访问到队列中间的元素。另一个单调队列的题可以看http://blog.csdn.net/nicholas_pzq/article/details/78609853
我们用cows这个数组记录牛的身高。
我们实现一个结构体Queue,其中用数组val存储值(在这个题里面存储到cows的索引),用front和back保存队列尾(back在最后一个元素后面紧邻的位置)。我们对于这个结构体只需要实现一个push函数,代表将新元素加入到Queue中,它返回一个int型的值。
对于push函数的详细说明为:在插入时,因为要实现降序,所以从队列尾开始,如果发现一个数在cows中的值大于队列中的这个值,那么就把这个值清除出队列(这个操作不会引发问题,在后面我们会看到原因)。返回值由我们的需求定。在本题中,因为我们要记录最多能看到多少头牛,那么有如下两种情况:(1)这个数的位置就是front,那么说明这个是迄今最大的数,可以看到东方所有的牛,返回0;(2)这个数的位置不是front,那么我们需要找到离它最近的、在cows中对应的数大于等于它的第一个数,事实上,这个值就位于刚插入的数的前面的位置。这也就解释了为什么我们在插入过程中可以把小的数统统删掉,因为当我们有了一个更新的更大的数,老而且小的数就不会对结果产生影响了。
下面来看实现
- #include <iostream>
- #include <cstring>
- using namespace std;
- const int N = 100000;
- int ans[N]; //能看到多少
- int cows[N]; //牛的身高
- struct Queue {
- int val[N];
- int front;
- int back;
- int len;
- Queue(int l) :front(0), back(0), len(2 * l) {}
- int push(int index) {
- if (front == back) { //初始情况
- val[back++] = index;
- return 0;
- }
- for (int i = back - 1; i >= front; i--) //删除小的数
- if (cows[index] > cows[val[i]])
- back--;
- val[back++] = index;
- if (back - front == 1) //如果这个数是最大的
- return 0;
- else
- return val[back - 2] - val[back - 1];//否则直接找到和前面的不小于他的之间的差距
- }
- };
- int main()
- {
- int n; cin >> n;
- for (int i = 0; i < n; i++)
- scanf("%d", &cows[i]);
- Queue q(n);
- for (int i = n - 1; i >= 0; i--) {
- int k = q.push(i);
- if (k == 0)
- ans[i] = n - 1 - i; //东侧一切牛都能看到
- else
- ans[i] = k - 1; //没有在push中减1是为了避免和0的情况重合
- }
- long long sum = 0; //一开始用的int没有过,改了就过了
- for (int i = 0; i < n; i++)
- sum += ans[i];
- cout << sum << endl;
- return 0;
- }