5033(詳解+維護斜率的單調棧+角度計算)
阿新 • • 發佈:2018-12-17
題目大意:T個測試用例,N個建築物,Q條查詢,輸出查詢的每個位置 能看到天空左右視角之和是多少,每條詢問的位置左右必定是有建築物的。
大致思路:維護一個斜率和高度(也就是斜率)的單調棧,將所有的點一起計算,將查詢點高度賦值為0即可。通過高度差與點差計算出斜率,從單邊進行計算,計算出左側的角度,將陣列反轉或者從右側出發,計算右側的角度,注意角度是與垂線之間的夾角,不是水平的。每加入一個點,維護棧內的高度和斜率,也就是這個人能看到棧內所有的建築物,如果看不到 ,被擋道就刪掉,刪除凹點即可。維護一個斜率絕對值遞增的單調棧
這種情況所有點都被 擋住了
這種情況都沒有被擋住所以直接算棧頂元素即可
所有的詳解以及細節都在程式碼的註釋裡,與君共勉。
#include<bits/stdc++.h> using namespace std; const int maxn=100100; int n; #define PI acos(-1) //圓周率,以前並沒用過 struct node{ int id; double h; double xi; }; //id表示查詢的下標,其hi=0,xi=qi; node a[2*maxn]; node s[2*maxn]; double ans[2*maxn]; int cmp(node a,node b) { return a.xi<b.xi; } double judge(node a,node b,node c) { double an1,an2; an1=((a.h-c.h)/(a.xi-c.xi));//(top-1,top,i);順序不要錯 an2=((b.h-c.h)/(b.xi-c.xi)); return an1<an2;//因為從分析單側所以所有的ans都為負。 //返回1則表示有凹點 } double digistangle(node a,node b) { double ans; ans=abs((a.xi-b.xi)/(a.h-b.h)); double angle=atan(ans); return angle*180/PI;//atan得出的弧度,這是轉化為角度的公式 } //把所有的查詢點作為一個獨立的點放進去,用hi來作為判斷; void solve(int cnt) { int top=0;//棧頂標記,始終指向棧頂的下一個下標。 for(int i=0;i<cnt;i++) { if(a[i].h==0)//判斷是否為查詢點 { // printf("top %d\n",judge(s[top],s[top-1],a[i])); while(top>=2&&judge(s[top-1],s[top],a[i])) { top--; }//同樣去除凹點; ans[a[i].id]+=digistangle(a[i],s[top]); } else { // 1 2 //2 1 // 5 1 // 1 //4//測試了很多資料 while(top&&a[i].h>=s[top].h)//如果棧非空的話 top--;//維護一個斜率以及高度的單調棧,將沒有用的凹點去掉即可 while(top>=2&&judge(s[top-1],s[top],a[i])) top--;//去除凹點; s[++top]=a[i]; } } } void revers(int n) { int mid; if(n%2==0) mid=n/2-1; else mid=n/2; for(int i=0;i<=mid;i++) { node p=a[i]; a[i]=a[n-i-1]; a[n-i-1]=p; } }//手動實現的反轉 // int main() { int T; scanf("%d",&T); int flag=0; while(T--) { scanf("%d",&n); for(int i=0;i<n;i++) {scanf("%lf %lf",&a[i].xi,&a[i].h); } int cas; int cnt=n; scanf("%d",&cas); double q; for(int i=0;i<cas;i++) { scanf("%lf",&q); a[cnt].xi=q; a[cnt].h=0; a[cnt].id=i; cnt++; } sort(a,a+cnt,cmp); //將下標從小到大來進行排序。 memset(ans,0,sizeof(ans)); solve(cnt); reverse(a,a+cnt); //revers(cnt); for(int i=0;i<cnt;i++) a[i].xi=10000000-a[i].xi ; //將原本的雙邊問題轉到單邊問題,簡單的轉化,棧就是單邊的 //並且因為judge函式判斷的是單側負數,用一個大數用來維持正負 solve(cnt); printf("Case #%d:\n",++flag); for(int i=0;i<cas;i++) { printf("%.10lf\n",ans[i]); } } return 0; } //題目雖然不難,只是維護一個斜率的單調棧,但是裡面需要注意的點卻很多
參考部落格: