zoukankan      html  css  js  c++  java
  • 音乐会的等待-单调栈(进阶版)

    这道题的基础做法在上一篇博文中已经提到了,详情请见:https://www.cnblogs.com/yufenglin/p/10306366.html

    而在课上有人提到了一种简便做法(错的,但可以改对),先来回顾一下题目:

    题目描述

    N个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的。

    写一个程序计算出有多少对人可以互相看见。

    输入输出格式

    输入格式:

    输入的第一行包含一个整数N (1 ≤ N ≤ 500 000), 表示队伍中共有N个人。

    接下来的N行中,每行包含一个整数,表示人的高度,以毫微米(等于10的-9次方米)为单位,每个人的调度都小于2^31毫微米。这些高度分别表示队伍中人的身高。

    输出格式:

    输出仅有一行,包含一个数S,表示队伍中共有S对人可以互相看见。

    输入输出样例

    输入样例#1: 复制
    7 
    2 
    4 
    1 
    2 
    2 
    5 
    1
    输出样例#1: 复制
     10
     
    简便做法:不进行二分查找,认为弹出元素的数量加一就是能看到的人数
      乍一看好像是对的:例如 9 8 7 3 2中插入一个6,那么弹出3,2看到了7,3,2,确实是弹出元素数量加一,但是忽略了相等的情况(题中没说不能相等(坑))。
      例如:9,8,7,2中插入一个2,那么显然不会弹出任何元素,但是看到的是两个哇!因此加入特判一。。。
      再例如:φ 中插入任何元素,那么显然答案是0,但是按照上述做法求得答案1!因此加入特判二。。。
      还有很多就不一一列举了,总之,如果一道题加了这么多特判,代码长度一定会大大增加,这里还是提供一种加了补丁后的代码:
      这里用到了两个栈,一个存数,另一个存个数。
     1 #include <cstdio>
     2 #include <stack>
     3 using namespace std;
     4 stack <long long> stk,num;
     5 int n;
     6 long long ans;
     7 int main()
     8 {
     9     scanf("%d",&n);
    10     long long tmp;
    11     for(int i = 1; i <= n; i++)
    12     {
    13         scanf("%lld",&tmp);
    14         while (!stk.empty() && stk.top() < tmp)
    15         {
    16             stk.pop();
    17             ans += num.top();
    18             num.pop();
    19         }
    20         if (stk.empty())
    21         {
    22             stk.push(tmp);
    23             num.push(1);
    24         }
    25         else
    26         {
    27             if (tmp != stk.top())
    28             {
    29                 ans += 1;
    30                 stk.push(tmp);
    31                 num.push(1);
    32             }
    33             else if (stk.size() == 1)
    34             {
    35                 ans += num.top();
    36                 int u = num.top();
    37                 num.pop();
    38                 num.push(u + 1);
    39             }
    40             else
    41             {
    42                 ans += num.top();
    43                 int u = num.top();
    44                 num.pop();
    45                 ans += 1;
    46                 num.push(u + 1);
    47             }
    48         }
    49     }
    50     printf("%lld
    ",ans);
    51     return 0;
    52 }

    接下来是答案的做法:

     1 #include<cstdio>
     2 using namespace std;
     3 int n,top;
     4 long long Ans;
     5 int a[500050],stk[500050];
     6 void dfs(int x)
     7 {
     8     int le=0,ri=top,mid,ret=0;
     9     while(le<=ri)
    10     {
    11         mid=(le+ri)>>1;
    12         if(a[stk[mid]]>x)ret=mid,le=mid+1;
    13         else ri=mid-1;
    14     }
    15     if(!ret)Ans+=top;
    16     else Ans+=top-ret+1;
    17 }
    18 int main()
    19 {
    20     scanf("%d",&n);
    21     for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
    22     for(int i=1; i<=n; ++i)
    23     {
    24         dfs(a[i]);
    25         while(top>0&&a[i]>a[stk[top]])--top;
    26         stk[++top]=i;
    27     }
    28     printf("%lld",Ans);
    29     return 0;
    30 }

    那么,答案为了避免上述情况,十分巧妙将两个操作分了开来,并且设置了一个ret值用来判断该栈中元素是否都比插入元素小,从而避免了多种情况的特判。

    因此,这道题也算是一个警示:在设计算法是一定要找特殊情况(反数据)来判断算法是否可行。

  • 相关阅读:
    HTML笔记
    html文本格式化
    标题大小与字体大小的关系
    html学习笔记
    冒泡排序
    直接插入算法
    绘制针状图
    绘制矢量图
    饼图pie 或者pie3
    三维直方图
  • 原文地址:https://www.cnblogs.com/yufenglin/p/10306624.html
Copyright © 2011-2022 走看看