1018 - 計算幾何之凸包 - 水平可見直線(BZOJ 1007)
阿新 • • 發佈:2018-11-10
分析
遇到新的題,要思考著往學過的模型上套
這道題要求的是水平可見直線,其實稍微轉化一下,就會發現最後能夠被看見的直線恰好形成了一個開口向上的半凸包的樣子
我們畫出一個半凸包,可以發現每條邊的斜率是在從左往右依次增加,而其交點橫座標的大小也在遞增
可以利用這個性質,對這道題進行求解
將直線按斜率從小到大的順序排序,依次加入棧中,噹噹前直線與棧頂直線的交點位於棧中前兩條直線交點的左邊時,說明棧頂直線會被新加入的直線所擋住,彈出棧頂元素
這樣一直做下去,最後留在棧中的直線就是可見的
最後。別忘了判重,思考極端資料:
有毒吧……建議大家到洛谷上去交這道題,BZOJ的資料水啊
程式碼
#include<bits/stdc++.h> #define N 500009 #define in read() #define eps 1e-8 using namespace std; inline int read(){ char ch;int f=1,res=0; while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return f==1?res:-res; } struct point { double x,y; }; struct Line{ double k,b; int id; }line[N]; int n; double a,b; bool cmp(const Line &a,const Line &b){ if(fabs(a.k-b.k)<eps) return a.b<b.b; return a.k<b.k; } bool cmp2(const Line &a,const Line &b) { return a.id<b.id; } Line s[N]; double ask(Line a,Line b){ return (b.b-a.b)/(a.k-b.k); } int main(){ n=in; int i,j; for(i=1;i<=n;++i) { scanf("%lf%lf",&a,&b); line[i].k=a;line[i].b=b; line[i].id=i; } if(n==1) {printf("1 ");return 0;} if(n==2) { int num=(line[2].b-line[1].b>eps)?2:1; if(fabs(line[1].k-line[2].k)<eps) printf("%d ",num); else printf("1 2 "); return 0; } sort(line+1,line+n+1,cmp); double inter; int qn=0; s[++qn]=line[1]; for(i=2;i<=n;++i){ if(fabs(line[i].k-s[qn].k)<eps) qn--; while(qn>1&&ask(line[i],s[qn] )<=ask(s[qn],s[qn-1])) qn--; s[++qn]=line[i]; } sort(s+1,s+qn+1,cmp2); for(i=1;i<=qn;++i) printf("%d ",s[i].id); return 0; }
最後一點WA的程式碼
#include<bits/stdc++.h> #define N 500009 #define in read() #define eps 1e-8 using namespace std; inline int read(){ char ch;int f=1,res=0; while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return f==1?res:-res; } struct point { double x,y; }; struct Line{ double k,b; int id; }line[N]; int n; double a,b; bool cmp(const Line &a,const Line &b){ if(fabs(a.k-b.k)<eps) return a.b<b.b; return a.k<b.k; } bool cmp2(const Line &a,const Line &b) { return a.id<b.id; } Line s[N]; double ask(Line a,Line b){ return (b.b-a.b)/(a.k-b.k); } int main(){ n=in; int i,j; for(i=1;i<=n;++i) { scanf("%lf%lf",&a,&b); line[i].k=a;line[i].b=b; line[i].id=i; } if(n==1) {printf("1 ");return 0;} if(n==2) { int num=(line[2].b-line[1].b>eps)?2:1; if(fabs(line[1].k-line[2].k)<eps) printf("%d ",num); else printf("1 2 "); return 0; } sort(line+1,line+n+1,cmp); double inter; int qn=0; s[++qn]=line[1];s[++qn]=line[2]; for(i=3;i<=n;++i){ if(fabs(line[i].k-s[qn].k)<eps) qn--;//如果第一條邊不合法,則不會被彈掉,改為while即可 while(qn>1&&ask(line[i],s[qn] )<=ask(s[qn],s[qn-1])) qn--; s[++qn]=line[i]; } sort(s+1,s+qn+1,cmp2); for(i=1;i<=qn;++i) printf("%d ",s[i].id); return 0; }