單調(diào)棧的維護(building)

本來以為是一道簡單題誰知道結(jié)果一直超時所以不得不上網(wǎng)搜了一下思路,原來使用的是單調(diào)棧蕴侧。
單調(diào)棧的主要特點表現(xiàn)為不斷進棧出棧使棧內(nèi)元素保持一定的單調(diào)性,在查找最大值或者最小值時對時間的減小用很大的作用。
題意: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 排序沪曙。。

  從左往右掃萎羔,單調(diào)棧維護斜率小的液走。。

  從右往左掃,單調(diào)棧維護斜率大的缘眶。嘱根。

題意分析:

首先我們可以知道在輸入數(shù)據(jù)后得到的是在數(shù)軸上高低不平的樓房,其中有一個坐標上代表的是人的位置:

這些樓房的位置和人的坐標都可以按照從左到右的坐標排序巷懈,從而在結(jié)構(gòu)體數(shù)組得到如上圖所示的結(jié)果该抒。
既然求的是最大的天空度數(shù),那么應該保留到達位置差與高度差的最大比顶燕,該比即為最大角度的正切值凑保。
可以根據(jù)單調(diào)棧的維護來進行數(shù)據(jù)處理。

A1

A2

如圖:首先將兩個元素進棧涌攻,然后從第三個開始欧引,判斷角度,如果前面的小于后面的癣漆,則不用出棧维咸,因為棧頂元素總為較大的,否則惠爽,后面的元素出棧癌蓖,出棧后,棧頂元素即仍為較大的角度婚肆,最后將當前元素入棧租副,便于下次判斷。上圖所示:第一附圖經(jīng)比較2》1较性,那么只進棧3用僧,若第二幅圖的情況,則需要出棧2赞咙,再進棧3责循。
依次類推判斷,直到遇到了人的位置攀操,那么就需要將人的位置作為當前位置院仿,與棧頂?shù)脑剡M行正切值的對比,較大的正切值則作為左邊的答案存放在答案數(shù)組中速和。
由此推斷可以得出歹垫,在棧內(nèi)存放的數(shù)據(jù)形態(tài)應該如下所示:


圖1

假設(shè)這是棧的原始狀態(tài),那么想要向后遍歷原始數(shù)組颠放,那么下一個樓房會出現(xiàn)兩種狀態(tài)排惨,
一、比當前的樓房矮碰凶。
二暮芭、比當前樓房高鹿驼,這種情況又會出現(xiàn)上述的兩種是否需要維護棧的問題。
具體圖示:
而當有一個比棧頂?shù)脑馗叩臉欠砍霈F(xiàn)谴麦,則會有:


情況一

而后一直出棧蠢沿,直到也獲得最大的夾角出現(xiàn),便為新的棧內(nèi)數(shù)據(jù)狀態(tài):


在圖一時匾效,若遇到第一種狀況舷蟀,那么只需要將當前元素進棧即可:


情況二

當遇到第二種狀況時,照例出棧
情況三

出棧后仍需繼續(xù)判斷與棧頂?shù)慕嵌汝P(guān)系面哼,并確定是否仍舊需要繼續(xù)出棧:


如圖所示情況野宜,不需出棧。
當遇到人所站的位置之后魔策,需首先判斷最大角匈子,然后將該最大角加入自己的答案數(shù)組,當做一邊的角闯袒,由于人的高度為零虎敦,任何情況下都會有樓房比零高,所以可以選擇不入棧政敢。
另一邊的角則需要利用reverse函數(shù)將原始數(shù)組反轉(zhuǎn)其徙,再從左向右判斷,直到找到所有的人的位置所在的右邊的角喷户,并加入答案數(shù)組唾那,依次輸出答案數(shù)組,此題AC褪尝。
然后便是簡單的代碼解讀:
首先必須要寫的函數(shù)是角度的比較問題闹获,即三個點,兩個棧內(nèi)河哑,一個棧外避诽,根據(jù)角度判斷棧頂是否需要出棧,那么需要把棧頂和棧頂下面的元素進行比較璃谨,公式為:假設(shè)棧頂為b茎用,前一個為a,預進棧的元素為c睬罗。
根據(jù)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);  
}  

然后進行維護棧函數(shù)的構(gòu)造:
若當前預進棧元素是人的坐標,則一定是比前面的元素低的垂券,故只需判斷是否需出棧元素即可花盐,調(diào)用check函數(shù)判斷羡滑,若不用,直接將最大角放入答案數(shù)組算芯,否則柒昏,不斷判斷是否需出棧,直到不需出棧熙揍,即找到了當前的最大角度职祷,再加入答案數(shù)組。

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函數(shù)表示把角度計算出來的構(gòu)造函數(shù)届囚。
如果不是人的坐標有梆,那么需判斷是否高于棧頂,如果是意系,則出棧泥耀,直到不再高于 ,然后進行低于棧頂情況的判斷和運算蛔添。

            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;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市夹攒,隨后出現(xiàn)的幾起案子蜘醋,更是在濱河造成了極大的恐慌,老刑警劉巖咏尝,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件压语,死亡現(xiàn)場離奇詭異,居然都是意外死亡编检,警方通過查閱死者的電腦和手機胎食,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來允懂,“玉大人厕怜,你說我怎么就攤上這事±僮埽” “怎么了粥航?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長生百。 經(jīng)常有香客問我递雀,道長,這世上最難降的妖魔是什么蚀浆? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任缀程,我火速辦了婚禮搜吧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杨凑。我一直安慰自己滤奈,他們只是感情好,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布撩满。 她就那樣靜靜地躺著蜒程,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹦牛。 梳的紋絲不亂的頭發(fā)上搞糕,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機與錄音曼追,去河邊找鬼窍仰。 笑死,一個胖子當著我的面吹牛礼殊,可吹牛的內(nèi)容都是我干的驹吮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼晶伦,長吁一口氣:“原來是場噩夢啊……” “哼碟狞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婚陪,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤族沃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泌参,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脆淹,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年沽一,在試婚紗的時候發(fā)現(xiàn)自己被綠了盖溺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡铣缠,死狀恐怖烘嘱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝗蛙,我是刑警寧澤蝇庭,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站捡硅,受9級特大地震影響遗契,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜病曾,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一牍蜂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泰涂,春花似錦鲫竞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至是牢,卻和暖如春僵井,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驳棱。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工批什, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人社搅。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓驻债,卻偏偏與公主長得像,于是被迫代替她去往敵國和親形葬。 傳聞我的和親對象是個殘疾皇子合呐,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Java byte code 的學習意義 為啥要學java bytecode,這就跟你問我已經(jīng)會python了為...
    shanggl閱讀 1,650評論 0 3
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 1,966評論 0 7
  • 《ilua》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 1...
    葉染柒丶閱讀 10,592評論 0 11
  • 棧 棧的英文單詞是Stack,它代表一種特殊的線性表笙以,這種線性表只能在固定一端(通常認為是線性表的尾端)進行插入淌实,...
    Jack921閱讀 1,496評論 0 5
  • 其實本不打算寫 2016 年總結(jié)的拆祈,只想把目光放到 2017 年的當下。但是應娟娟的提議谈息,還是去翻了翻自己 201...
    梅小歪閱讀 255評論 0 0