单调栈的维护(building)

本来以为是一道简单题谁知道结果一直超时所以不得不上网搜了一下思路,原来使用的是单调栈。
单调栈的主要特点表现为不断进栈出栈使栈内元素保持一定的单调性,在查找最大值或者最小值时对时间的减小用很大的作用。
题意:N 幢楼排成一列(1<=N<=10^5),各楼有横坐标 xi(1<=xi<=10^7) 以及高度 hi(1<=hi<=107),在各楼之间的Q个位置(1<=Q<=105),问这些位置可以仰望天空的夹角是多少度。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5033

——>>将楼和人的位置一起按 x 排序。。

  从左往右扫,单调栈维护斜率小的。。

  从右往左扫,单调栈维护斜率大的。。

题意分析:

首先我们可以知道在输入数据后得到的是在数轴上高低不平的楼房,其中有一个坐标上代表的是人的位置:

这些楼房的位置和人的坐标都可以按照从左到右的坐标排序,从而在结构体数组得到如上图所示的结果。
既然求的是最大的天空度数,那么应该保留到达位置差与高度差的最大比,该比即为最大角度的正切值。
可以根据单调栈的维护来进行数据处理。

A1

A2

如图:首先将两个元素进栈,然后从第三个开始,判断角度,如果前面的小于后面的,则不用出栈,因为栈顶元素总为较大的,否则,后面的元素出栈,出栈后,栈顶元素即仍为较大的角度,最后将当前元素入栈,便于下次判断。上图所示:第一附图经比较2》1,那么只进栈3,若第二幅图的情况,则需要出栈2,再进栈3。
依次类推判断,直到遇到了人的位置,那么就需要将人的位置作为当前位置,与栈顶的元素进行正切值的对比,较大的正切值则作为左边的答案存放在答案数组中。
由此推断可以得出,在栈内存放的数据形态应该如下所示:


图1

假设这是栈的原始状态,那么想要向后遍历原始数组,那么下一个楼房会出现两种状态,
一、比当前的楼房矮。
二、比当前楼房高,这种情况又会出现上述的两种是否需要维护栈的问题。
具体图示:
而当有一个比栈顶的元素高的楼房出现,则会有:


情况一

而后一直出栈,直到也获得最大的夹角出现,便为新的栈内数据状态:


在图一时,若遇到第一种状况,那么只需要将当前元素进栈即可:


情况二

当遇到第二种状况时,照例出栈
情况三

出栈后仍需继续判断与栈顶的角度关系,并确定是否仍旧需要继续出栈:


如图所示情况,不需出栈。
当遇到人所站的位置之后,需首先判断最大角,然后将该最大角加入自己的答案数组,当做一边的角,由于人的高度为零,任何情况下都会有楼房比零高,所以可以选择不入栈。
另一边的角则需要利用reverse函数将原始数组反转,再从左向右判断,直到找到所有的人的位置所在的右边的角,并加入答案数组,依次输出答案数组,此题AC。
然后便是简单的代码解读:
首先必须要写的函数是角度的比较问题,即三个点,两个栈内,一个栈外,根据角度判断栈顶是否需要出栈,那么需要把栈顶和栈顶下面的元素进行比较,公式为:假设栈顶为b,前一个为a,预进栈的元素为c。
根据A1A2,判断是否:
(a.x-c.x)/(a.h-c.h)>=(b.x-c.x)/(b.h-c.h)。整理得:
(a.h - c.h) (c.x - b.x) >= (ll)(b.h - c.h) * (c.x - a.x)。
故有:

int check(Node a, Node b, Node c)  
{  
    return (ll)(a.h - c.h) * (c.x - b.x) >= (ll)(b.h - c.h) * (c.x - a.x);  
}  

然后进行维护栈函数的构造:
若当前预进栈元素是人的坐标,则一定是比前面的元素低的,故只需判断是否需出栈元素即可,调用check函数判断,若不用,直接将最大角放入答案数组,否则,不断判断是否需出栈,直到不需出栈,即找到了当前的最大角度,再加入答案数组。

if (node[i].h <= 0)  
 {  
          while (head >= 2 && check(stk[head - 2], stk[head - 1], node[i]))  
           head--;  
           ans[i] += ang(stk[head - 1], node[i]);  
  }  

ang函数表示把角度计算出来的构造函数。
如果不是人的坐标,那么需判断是否高于栈顶,如果是,则出栈,直到不再高于 ,然后进行低于栈顶情况的判断和运算。

            while (head && stk[head - 1].h <= node[i].h)  
                head--;  

否则,判断是否出栈,是则不断出,否则将当前元素入栈:

        while (head >= 2 && check(stk[head - 2], stk[head - 1], node[i]))  
            head--;  
        stk[head++] = node[i];  

有了这几个判断这道题便非常明了了,下面的代码便也可以轻易明白了:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>

typedef long long ll;
using namespace std;

const double PI = acos(-1.0);
const int maxn = 200100;
const int inf = 1e8;

struct Node
{
    int x, h;
    bool operator <(const Node &a)const
    {
        return x < a.x;
    }
} node[maxn], stk[maxn];

double ans[maxn];
int n, q;

int check(Node a, Node b, Node c)
{
    if (c.h <= 0)
        c.h = 0;
    return (ll)(a.h - c.h) * (c.x - b.x) >= (ll)(b.h - c.h) * (c.x - a.x);
}

double ang(Node a, Node b)
{
    return atan(1.0 * (b.x - a.x) / a.h);
}

void solve()
{
    int head = 0;
    for (int i = 0; i < n + q; i++)
    {
        if (node[i].h <= 0)
        {
            while (head >= 2 && check(stk[head - 2], stk[head - 1], node[i]))
                head--;
            ans[-node[i].h] += ang(stk[head - 1], node[i]);
        }
        else
        {
            while (head && stk[head - 1].h <= node[i].h)
                head--;
            while (head >= 2 && check(stk[head - 2], stk[head - 1], node[i]))
                head--;
            stk[head++] = node[i];
        }
    }
}

int main()
{
    int t, cas = 1;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        for (int i = 0; i < n; i++)
            scanf("%d%d", &node[i].x, &node[i].h);
        scanf("%d", &q);
        for (int i = 0; i < q; i++)
        {
            scanf("%d", &node[i + n].x);
            node[i + n].h = -i;
        }
        memset(ans, 0, sizeof(ans));
        sort(node, node + n + q);
        solve();
        reverse(node, node + n + q);
        for (int i = 0; i < n + q; i++)
            node[i].x = inf - node[i].x;
        solve();
        printf("Case #%d:\n", cas++);
        for (int i = 0; i < q; i++)
            printf("%.10lf\n", ans[i] * 180.0 / PI);
    }
    return 0;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • Java byte code 的学习意义 为啥要学java bytecode,这就跟你问我已经会python了为...
    shanggl阅读 1,641评论 0 3
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,944评论 0 7
  • 《ilua》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 1...
    叶染柒丶阅读 10,417评论 0 11
  • 栈 栈的英文单词是Stack,它代表一种特殊的线性表,这种线性表只能在固定一端(通常认为是线性表的尾端)进行插入,...
    Jack921阅读 1,487评论 0 5
  • 其实本不打算写 2016 年总结的,只想把目光放到 2017 年的当下。但是应娟娟的提议,还是去翻了翻自己 201...
    梅小歪阅读 251评论 0 0