1. 程式人生 > >【HDU 5033】【經典單調棧問題】Building

【HDU 5033】【經典單調棧問題】Building

題意:

n個人,n棟樓,問每個人往左看,往右看,能夠看到的天空的最大角度。

【這是一個很經典的單調棧問題】

 

思路:

      首先,能夠想到將人和樓全部讀入棧中進行統一處理。並且左邊跑一次單調棧,求出人能夠往左看到的最高的樓。右邊跑一次,處理出人能夠往右看到的最高的樓房。【reverse(a+1,a+1+num)即可換方向再跑一遍】

      問題在於如何處理出每個人能夠看到的最高樓房。

 

      容易想到,棧內元素的樓高一定是依次遞減的,維護遞減序列。

      並且維護棧內相鄰元素斜率遞增,這樣才能保證對於棧內第i個點,它能夠看到的最高的樓房就是i-1所代表的樓房。

      這樣按照兩個遞減方案維護出的單調棧即可求出所有節點能夠看到的最遠的樓房。

 

注意:

      atan()函式為反三角函式,求出的值是弧度值,需要*180/pai轉換為角度值。const double pai = 3.141592654。

 

總結:

      總結一下單調棧和單調佇列。

      單調佇列:主要應用k區間問題,即滑動視窗,求出k區間內的最大值、最小值、sum和。

      單調棧:維護棧內單調性,求出每個節點往右或往左的最大值、最小值(可以根據自己定義的出入棧規則維護題目所需的最大值、最小值)。

 

程式碼:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <cmath>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
const int N = 1e5+100;
const double pai = 3.141592654;

int n,m,num,cnt;
double query[N];
pair<double,double> a[2*N];
map<double,double> mp;
int stack[2*N];

void solve()
{
	stack[0] = 0;
	int tp = 0;
	rep(i,1,num)
	{
		while(tp > 0 && a[i].second > a[stack[tp]].second)
			tp--;
		while(tp > 1)
		{
			double t1 = fabs(a[i].second-a[stack[tp]].second)/fabs(a[i].first-a[stack[tp]].first);
			double t2 = fabs(a[stack[tp]].second-a[stack[tp-1]].second)/fabs(a[stack[tp-1]].first-a[stack[tp]].first);
		//	printf("t1: %f, t2:%f\n",t1,t2);
			if(t1 >= t2) break;
			else{
				tp--;
			} 
		}
		stack[++tp] = i;		
		if(a[stack[tp]].second == 0)
		{
		//	printf("self: %f, building: %f\n",a[stack[tp]].first,a[stack[tp-1]].first);
			double ang = fabs(a[stack[tp-1]].second-a[stack[tp]].second)/fabs(a[stack[tp-1]].first-a[stack[tp]].first);
			mp[a[stack[tp]].first] += atan(ang)*180.0/pai;
		//	printf("ang: %f,add: %f\n",ang,mp[a[stack[tp]].first]);
		}
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	rep(kk,1,T)
	{
		a[0] = make_pair(0,0);
		mp.clear();
		num = cnt = 0;
		scanf("%d",&n);
		rep(i,1,n){
			int x,y;
			scanf("%d%d",&x,&y);
			a[++num] = make_pair(x,y);
		}
		scanf("%d",&m);
		rep(i,1,m){
			int x;
			scanf("%d",&x);
			query[++cnt] = x;
			a[++num] = make_pair(x,0);
		}
		sort(a+1,a+1+num);
		printf("Case #%d:\n",kk);
		solve();
		reverse(a+1,a+1+num);
		solve();
		rep(i,1,cnt){
			printf("%f\n",180.0-mp[query[i]]);
		}
	}
	return 0;
}
/*
	對於每一個點,只維護這個點能夠向左看到的最高樓,中間的所有樓都去掉
	對於x1 < x2,x1這個點能看到的最高樓,如果x1和x2中間沒有樓,x2一定可以看到
*/