小学半平面交
快到省选了,赶紧学一发计算几何……
这玩意就是线性规划.
半平面通常用不等式或者两点表示,具体表示方法,就题而言,要灵活.
算法的话,什么朴素O(n^2),什么分治,都不会,只会一个弹栈的.
这个算法的话,一开始好像一定要去重,而且有的时候需要在外面加4个框,似乎是因为这个算法在相邻直线的极角跨度大于Pi的时候就不对了……似乎还是为了保证判断无解的方法(剩余直线小于3个)的正确性……
反正我就大概懂一些计算几何基础(向量啊什么的),并且感性理解了一发这个算法……
就做了几道水题:
#include <cstdio> #include <cstring> #include <algorithm> const int N=10010; struct Race{ double k,b; int id; }race[N],stack[N]; int top; inline bool comp(Race a,Race b){ return a.k<b.k||(a.k==b.k&&a.b>b.b); } int n; bool good[N]; inline void Init(){ scanf("%d",&n); int i; for(i=1;i<=n;++i) scanf("%lf",&race[i].b); for(i=1;i<=n;++i) scanf("%lf",&race[i].k),race[i].id=i; } inline bool check(Race a,Race b,Race c){ double x=(double)(b.b-a.b)/(a.k-b.k); double y=a.k*x+a.b; return y>=c.k*x+c.b; } inline void Work(){ int i,j; std::sort(race+1,race+(n+1),comp); j=0; race[++j]=race[1]; for(i=2;i<=n;++i) if(race[i].k!=race[j].k||(race[i].k==race[j].k&&race[i].b==race[j].b)) race[++j]=race[i]; stack[++top]=(Race){1./0.,0.,0}; for(i=1;i<=j;++i){ while(top>1&&!check(stack[top],stack[top-1],race[i]))--top; stack[++top]=race[i]; } } inline void Print(){ int i; for(i=1;i<=top;++i) good[stack[i].id]=true; printf("%d\n",top-1); for(i=1;i<=n;++i) if(good[i]) printf("%d ",i); puts(""); } int main(){ Init(),Work(),Print(); return 0; }
#include <cstdio> #include <cstring> #include <algorithm> const int N=110; const double oo=1e10; const double eps=1e-6; int n; double s; struct Peo{ double k,b; inline void redef(double x,double y){ k=1./x-1./y; b=s/y; } inline double query(double pos){ return k*pos+b; } }peo[N]; inline double query(double pos){ double ret=oo; for(int i=1;i<n;++i) ret=std::min(ret,peo[i].query(pos)); ret-=peo[n].query(pos); return ret; } int main(){ scanf("%lf%d",&s,&n); int i; double l,r,mid1,mid2,x,y,ans1=0.,ans2=-oo; for(i=1;i<=n;++i){ scanf("%lf%lf",&x,&y); x/=3600,y/=3600; peo[i].redef(x,y); } l=0.,r=s; while(l+eps<r){ mid1=(r-l)/3+l; mid2=r-(r-l)/3; x=query(mid1); y=query(mid2); if(x<=y) ans2=y,ans1=mid2,l=mid1; else ans2=x,ans1=mid1,r=mid2; } x=query(0); if(x>=ans2)ans2=x,ans1=0; if(ans2<0)puts("NO"); else printf("%.2f %.2f %.0f\n",ans1,s-ans1,ans2); return 0; }
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> const int N=310; const double oo=1e10; const double eps=1e-8; struct Poi{ double x,y; inline friend double operator *(Poi a,Poi b){ return a.x*b.y-a.y*b.x; } inline friend Poi operator -(Poi a,Poi b){ return (Poi){a.x-b.x,a.y-b.y}; } }dia[N]; struct Line{ Poi s,t; double k; inline void redef(Poi a,Poi b){ s=a,t=b,k=std::atan2(b.y-a.y,b.x-a.x); } }line[N],stack[N]; int top; inline bool comp(Line a,Line b){ return a.k==b.k?(b.s-a.s)*(b.t-a.s)>0:a.k<b.k; } int n; inline Poi point(Line a,Line b){ Poi ret; double s1=(b.s-a.s)*(b.t-a.s); double s2=(b.t-a.t)*(b.s-a.t); ret.x=(s1*a.t.x+s2*a.s.x)/(s1+s2); ret.y=(s1*a.t.y+s2*a.s.y)/(s1+s2); return ret; } inline bool check(Line a,Line b,Line c){ Poi poi=point(a,b); return (c.s-poi)*(c.t-poi)>0; } inline void Init(){ scanf("%d",&n); int i; for(i=1;i<=n;++i) scanf("%lf",&dia[i].x); for(i=1;i<=n;++i) scanf("%lf",&dia[i].y); dia[0]=(Poi){dia[1].x,oo}; dia[n+1]=(Poi){dia[n].x,oo}; ++n; for(i=1;i<=n;++i) line[i].redef(dia[i-1],dia[i]); } inline void Work(){ std::sort(line+1,line+(n+1),comp); int i,j; j=0; line[++j]=line[1]; for(i=2;i<=n;++i) if(line[i].k!=line[j].k) line[++j]=line[i]; stack[++top]=line[1]; stack[++top]=line[2]; for(i=3;i<=j;++i){ while(top>1&&!check(stack[top],stack[top-1],line[i]))--top; stack[++top]=line[i]; } } inline double query(double pos){ Poi p1=(Poi){pos,0},p2=(Poi){pos,oo},poi; Line tmp; tmp.redef(p1,p2); double ret=-oo; for(int i=2;i<top;++i){ poi=point(tmp,stack[i]); ret=std::max(ret,poi.y); } return ret; } inline void Print(){ int i,at; double ans=oo; Poi poi; for(i=1;i<n;++i) ans=std::min(ans,query(dia[i].x)-dia[i].y); at=1; for(i=3;i<top;++i){ poi=point(stack[i-1],stack[i]); while(poi.x>=dia[at+1].x)++at; ans=std::min(ans,poi.y-((dia[at+1].y-dia[at].y)*(poi.x-dia[at].x)/(dia[at+1].x-dia[at].x)+dia[at].y)); } printf("%.3f\n",ans); } int main(){ //freopen("rio.txt","r",stdin); //freopen("wq.txt","w",stdout); Init(),Work(),Print(); return 0; }
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> const int N=110; double s=1; int n,v[N],u[N],w[N]; struct Line{ double a,b,c,k; inline void redef(double x,double y,double z){ a=x,b=y,c=z; if(x<0)x=-x; if(x)a/=x,b/=x,c/=x; k=std::atan2(-a,b); } }line[N],queue[N]; int head,tail; inline bool comp(Line a,Line b){ return a.k!=b.k?a.k<b.k:a.c<b.c; } inline bool check(Line a,Line b,Line c){ double x=(b.c*a.b-a.c*b.b)/(a.a*b.b-a.b*b.a); double y=(b.c*a.a-a.c*b.a)/(a.b*b.a-a.a*b.b); return c.a*x+c.b*y+c.c>0; } inline void Init(){ scanf("%d",&n); int i; for(i=1;i<=n;++i) scanf("%d%d%d",&v[i],&u[i],&w[i]); } inline bool check(int id){ int m=5,i,j; line[1].redef(1,0,0); line[2].redef(-1,0,s); line[3].redef(0,1,0); line[4].redef(0,-1,s); line[5].redef(-1,-1,s); double a,b,c; for(i=1;i<=n;++i){ if(i==id)continue; if(v[id]<=v[i]&&u[id]<=u[i]&&w[id]<=w[i])return false; a=1./v[id]-1./v[i]; b=1./u[id]-1./u[i]; c=1./w[id]-1./w[i]; ++m; line[m].redef(c-a,c-b,-c*s); } std::sort(line+1,line+(m+1),comp); j=0; line[++j]=line[1]; for(i=2;i<=m;++i) if(line[i].k!=line[j].k) line[++j]=line[i]; head=tail=0; queue[tail++]=line[1]; queue[tail++]=line[2]; for(i=3;i<=j;++i){ while(tail-head>1&&!check(queue[tail-1],queue[tail-2],line[i]))--tail; while(tail-head>1&&!check(queue[head],queue[head+1],line[i]))++head; queue[tail++]=line[i]; } while(tail-head>1&&!check(queue[tail-1],queue[tail-2],queue[head]))--tail; return tail-head>2; } inline void Print(){ int i; for(i=1;i<=n;++i) puts(check(i)?"Yes":"No"); } int main(){ Init();Print(); return 0; }
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> const int N=100010; const double oo=1e20; int n,cnt; struct Line{ double a,b,c,k; int id; inline void redef(double x,double y,double z,int name){ //如果x、y都为0,那么若z>=0成立,则整张图都成立,若不成立,则整张图都不成立 id=name; a=x,b=y,c=z; if(x<0)x=-x; if(x)a/=x,b/=x,c/=x; else{ if(y<0)y=-y; if(y)a/=y,b/=y,c/=y; } k=std::atan2(-a,b); //讨论一下4种平行、外加Delta的计算,就会发现这是对的 } }keep[N<<1],line[N<<1],queue[N<<1]; int head,tail; inline bool comp(Line a,Line b){ return a.k!=b.k?a.k<b.k:a.c<b.c; //在我们上面进行了除x且除y的操作之后,这样是对的 } inline bool check(Line a,Line b,Line c){ double x=(a.b*b.c-b.b*a.c)/(b.b*a.a-a.b*b.a); double y=(a.a*b.c-b.a*a.c)/(b.a*a.b-a.a*b.b); return c.a*x+c.b*y+c.c>=0; //手推一下就能出来,只要记住x的主料为b、y的主料为a、上下主料相反、单项相反、上c下反即可 } inline bool check(int k){ int i,j=0; for(i=1;i<=cnt;++i) if(keep[i].id<=k&&(j==0||keep[i].k!=line[j].k)) line[++j]=keep[i]; head=tail=0; queue[tail++]=line[1]; queue[tail++]=line[2]; for(i=3;i<=j;++i){ while(tail-head>1&&!check(queue[tail-1],queue[tail-2],line[i]))--tail; while(tail-head>1&&!check(queue[head],queue[head+1],line[i]))++head; queue[tail++]=line[i]; } while(tail-head>1&&!check(queue[tail-1],queue[tail-2],queue[head]))--tail; return tail-head>=3; } inline void Init(){ cnt=4; keep[1].redef(1,0,oo,0); keep[2].redef(-1,0,0,0); keep[3].redef(0,1,oo,0); keep[4].redef(0,-1,oo,0); scanf("%d",&n); int i,j,x,y,z; for(i=1;i<=n;++i){ scanf("%d%d%d",&x,&y,&z); keep[++cnt].redef((double)x*x,x,-y,i); keep[++cnt].redef(-(double)x*x,-x,z,i); } std::sort(keep+1,keep+(cnt+1),comp); } inline void Work(){ int l=1,r=n,mid,ans=0; while(l<=r){ mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); } int main(){ Init(),Work(); return 0; }
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define pf(a) ((double)(a)*(a)) const int N=610; int n; struct V{ int to,next; }c[N*N*2]; int first[N],t; int di[N],qu[N],front,back; int aks[N],wai[N]; inline void add(int x,int y){ c[++t].to=y,c[t].next=first[x],first[x]=t; } struct Line{ double a,b,c,k; int id; inline void redef(double x,double y,double z,int name){ a=x,b=y,c=z,id=name; if(x<0)x=-x; if(x)a/=x,b/=x,c/=x; else{ if(y<0)y=-y; if(y)a/=y,b/=y,c/=y; } k=std::atan2(-a,b); } }line[N],queue[N]; int head,tail; inline bool comp(Line a,Line b){ return a.k!=b.k?a.k<b.k:a.c<b.c; } inline bool check(Line a,Line b,Line c){ double x=(a.b*b.c-b.b*a.c)/(b.b*a.a-a.b*b.a); double y=(a.a*b.c-b.a*a.c)/(b.a*a.b-a.a*b.b); return c.a*x+c.b*y+c.c>0; } inline void build(int id,int li,int im){ int i,j=1,m=4; line[1].redef(1,0,0,0); line[2].redef(-1,0,li,0); line[3].redef(0,1,0,0); line[4].redef(0,-1,im,0); double a,b,c; for(i=1;i<=n;++i){ if(i==id)continue; a=2*aks[id]-2*aks[i]; b=2*wai[id]-2*wai[i]; c=pf(aks[i])-pf(aks[id])+pf(wai[i])-pf(wai[id]); line[++m].redef(a,b,c,i); } std::sort(line+1,line+(m+1),comp); for(i=2;i<=m;++i) if(line[i].k!=line[j].k) line[++j]=line[i]; head=tail=0; queue[tail++]=line[1]; queue[tail++]=line[2]; for(i=3;i<=j;++i){ while(tail-head>1&&!check(queue[tail-1],queue[tail-2],line[i]))--tail; while(tail-head>1&&!check(queue[head],queue[head+1],line[i]))++head; queue[tail++]=line[i]; } while(tail-head>1&&!check(queue[tail-1],queue[tail-2],queue[head]))--tail; for(i=head;i<tail;++i) add(id,queue[i].id); } inline int bfs(int s){ memset(di,-1,sizeof(di)); front=back=0; qu[back++]=s; di[s]=0; int x,i; while(front!=back){ x=qu[front++]; for(i=first[x];i;i=c[i].next) if(di[c[i].to]==-1){ if(!c[i].to)return di[x]+1; di[c[i].to]=di[x]+1; qu[back++]=c[i].to; } } } inline void Main(){ memset(first,0,sizeof(first)),t=0; scanf("%d",&n); int i,w,q,l,m,id; double min,dis; scanf("%d%d%d%d",&l,&m,&w,&q); if(n==0){ puts("0"); return; } for(i=1;i<=n;++i){ scanf("%d%d",&aks[i],&wai[i]); dis=pf(w-aks[i])+pf(q-wai[i]); if(i==1||dis<min) id=i,min=dis; } for(i=1;i<=n;++i) build(i,l,m); printf("%d\n",bfs(id)); } int main(){ int T; scanf("%d",&T); while(T--) Main(); return 0; }
一般姿势正确就不会被卡精,但是被卡精卡到怀疑人生也是常有的事儿……做题的时候感觉二分/三分和半平面交有互相替代的关系……
苟利国家生死以, 岂因祸福避趋之。