音乐会的等待-单调栈(进阶版)
Posted yufenglin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了音乐会的等待-单调栈(进阶版)相关的知识,希望对你有一定的参考价值。
这道题的基础做法在上一篇博文中已经提到了,详情请见: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值用来判断该栈中元素是否都比插入元素小,从而避免了多种情况的特判。
因此,这道题也算是一个警示:在设计算法是一定要找特殊情况(反数据)来判断算法是否可行。
以上是关于音乐会的等待-单调栈(进阶版)的主要内容,如果未能解决你的问题,请参考以下文章
[COI2007] [luogu P1823] Patrik 音乐会的等待 解题报告 (单调栈)