队列需要活学活用,不可死板


分类:
open judge,poj,算法(6)
描述

农夫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中对应的数大于等于它的第一个数,事实上,这个值就位于刚插入的数的前面的位置。这也就解释了为什么我们在插入过程中可以把小的数统统删掉,因为当我们有了一个更新的更大的数,老而且小的数就不会对结果产生影响了。

     下面来看实现

    

  1. #include <iostream>  
  2. #include <cstring>  
  3. using namespace std;  
  4. const int N = 100000;  
  5. int ans[N];        //能看到多少  
  6. int cows[N];       //牛的身高  
  7. struct Queue {  
  8.     int val[N];  
  9.     int front;  
  10.     int back;  
  11.     int len;  
  12.     Queue(int l) :front(0), back(0), len(2 * l) {}  
  13.     int push(int index) {  
  14.         if (front == back) {                         //初始情况  
  15.             val[back++] = index;  
  16.             return 0;  
  17.         }  
  18.         for (int i = back - 1; i >= front; i--)      //删除小的数  
  19.             if (cows[index] > cows[val[i]])  
  20.                 back--;  
  21.         val[back++] = index;  
  22.         if (back - front == 1)                       //如果这个数是最大的  
  23.             return 0;  
  24.         else  
  25.             return val[back - 2] - val[back - 1];//否则直接找到和前面的不小于他的之间的差距  
  26.     }  
  27. };  
  28. int main()  
  29. {  
  30.     int n; cin >> n;  
  31.     for (int i = 0; i < n; i++)  
  32.         scanf("%d", &cows[i]);  
  33.     Queue q(n);  
  34.     for (int i = n - 1; i >= 0; i--) {  
  35.         int k = q.push(i);  
  36.         if (k == 0)   
  37.             ans[i] = n - 1 - i;       //东侧一切牛都能看到  
  38.         else   
  39.             ans[i] = k - 1;           //没有在push中减1是为了避免和0的情况重合  
  40.     }  
  41.     long long sum = 0;                        //一开始用的int没有过,改了就过了  
  42.     for (int i = 0; i < n; i++)  
  43.         sum += ans[i];  
  44.     cout << sum << endl;  
  45.     return 0;  
  46. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值