半平面交
简析
【详解】半平面交算法入门详解(计算几何)_Winsoul Blog-CSDN博客_半平面交
反例见小本本
需要铭记直线取左边
例题1
[P3222 HNOI2012]射箭 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
见小本本
#include<bits/stdc++.h>
#define db long double
const db eps=1e-11,INF=1e11;
using namespace std;
const int N=2e5+5;
int n,m,q[N];
struct Po{db x,y; }p[N];
inline db operator *(Po i,Po j) {
return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
return (Po){i.x*j,i.y*j};
}
inline Po operator /(Po i,db j) {
return (Po){i.x/j,i.y/j};
}
inline Po operator -(Po i,Po j) {
return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
return (Po){i.x+j.x,i.y+j.y};
}
struct Li{Po s,t; db ang; int id; }a[N];
inline bool cmp(Li i,Li j) {
return i.ang<j.ang;
}
inline bool Right(Li i,Po j) {
return (i.t-i.s)*(j-i.s)<-eps;
}
inline Po Crs(Li i,Li j) {
Po A=i.s,B=i.t,C=j.s,D=j.t;
db s1=(A-C)*(D-C),s2=(D-C)*(B-C);
return A+(B-A)*(s1/(s1+s2));
}
bool ask(int mid) {
int l=1,r=0;
for(int i=1;i<=m;i++) {
if(a[i].id>mid) continue;
while(l<r&&Right(a[i],p[r-1])) r--;
while(l<r&&Right(a[i],p[l])) l++;
if(l>r) {
q[++r]=i;
continue;
}
if(a[i].ang!=a[q[r]].ang) q[++r]=i;
else if(Right(a[i],a[q[r]].s)) q[r]=i;
if(l<r) p[r-1]=Crs(a[q[r-1]],a[q[r]]);
}
while(l<r&&Right(a[q[l]],p[r-1])) r--;
return r-l+1>=3;
}
int main() {
// freopen("1.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) {
db x,Y1,Y2; scanf("%Lf%Lf%Lf",&x,&Y1,&Y2);
a[++m]=(Li){(Po){0,Y1/x},(Po){1,Y1/x-x},atan2(-x,1),i};
a[++m]=(Li){(Po){1,Y2/x-x},(Po){0,Y2/x},atan2(x,-1),i};
}
a[++m]=(Li){(Po){-INF,eps},(Po){-eps,eps},atan2(0,INF),0};
a[++m]=(Li){(Po){-eps,eps},(Po){-eps,INF},atan2(INF,0),0};
a[++m]=(Li){(Po){-eps,INF},(Po){-INF,INF},atan2(0,-INF),0};
a[++m]=(Li){(Po){-INF,INF},(Po){-INF,eps},atan2(-INF,0),0};
sort(a+1,a+m+1,cmp);
int l=1,r=n,ans=0,mid;
while(l<=r) {
int mid=l+r>>1;
if(ask(mid)) {
ans=mid,l=mid+1;
} else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
例题2
[P4196 CQOI2006]凸多边形 /【模板】半平面交 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
#define db long double
const db eps=1e-9;
using namespace std;
const int N=1005;
int n,m,q[N];
struct Po{db x,y; }p[N],b[55];
inline db operator *(Po i,Po j) {
return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
return (Po){i.x*j,i.y*j};
}
inline Po operator /(Po i,db j) {
return (Po){i.x/j,i.y/j};
}
inline Po operator -(Po i,Po j) {
return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
return (Po){i.x+j.x,i.y+j.y};
}
struct Li{Po s,t; db ang; }a[N];
inline bool cmp(Li i,Li j) {
return i.ang<j.ang;
}
inline bool Right(Li i,Po j) {
return (i.t-i.s)*(j-i.s)<-eps;
}
inline Po Crs(Li i,Li j) {
Po A=i.s,B=i.t,C=j.s,D=j.t;
db s1=(A-C)*(D-C),s2=(D-C)*(B-C);
return A+(B-A)*(s1/(s1+s2));
}
inline db ask() {
int l=1,r=0;
for(int i=1;i<=m;i++) {
while(l<r&&Right(a[i],p[r-1])) r--;
while(l<r&&Right(a[i],p[l])) l++;
if(l>r) {
q[++r]=i;
continue;
}
if(a[i].ang!=a[q[r]].ang) q[++r]=i;
else if(Right(a[i],a[q[r]].s)) q[r]=i;
if(l<r) p[r-1]=Crs(a[q[r-1]],a[q[r]]);
}
while(l<r&&Right(a[q[l]],p[r-1])) r--;
if(r-l+1<=2) {
return 0;
}
p[r]=Crs(a[q[r]],a[q[l]]);
db ret=p[r]*p[l];
for(int i=l+1;i<=r;i++) {
ret+=p[i-1]*p[i];
}
return fabs(ret)/2;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
int K; scanf("%d",&K);
for(int j=1;j<=K;j++) {
scanf("%Lf%Lf",&b[j].x,&b[j].y);
}
Po t=b[1]-b[K];
a[++m]=(Li){b[K],b[1],atan2(t.y,t.x)};
for(int j=1;j<K;j++) {
t=b[j+1]-b[j];
a[++m]=(Li){b[j],b[j+1],atan2(t.y,t.x)};
}
}
sort(a+1,a+m+1,cmp);
printf("%.3Lf\n",ask());
return 0;
}
例题3
[P2600 ZJOI2008]瞭望塔 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
求出半平面交的范围
然后可以发现,两条线段在端点处靠近,枚举端点即可
#include<bits/stdc++.h>
#define db long double
const db eps=1e-9,INF=1e14;
using namespace std;
const int N=310;
int n,m,q[N];
struct Po{db x,y; }p[N],b[N];
inline db operator *(Po i,Po j) {
return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
return (Po){i.x*j,i.y*j};
}
inline Po operator /(Po i,db j) {
return (Po){i.x/j,i.y/j};
}
inline Po operator -(Po i,Po j) {
return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
return (Po){i.x+j.x,i.y+j.y};
}
struct Li{Po s,t; db ang; }a[N];
inline bool cmp(Li i,Li j) {
return i.ang<j.ang;
}
inline bool Right(Li i,Po j) {
return (i.t-i.s)*(j-i.s)<-eps;
}
inline Po Crs(Li i,Li j) {
Po A=i.s,B=i.t,C=j.s,D=j.t;
db s1=(A-C)*(D-C),s2=(D-C)*(B-C);
return A+(B-A)*(s1/(s1+s2));
}
inline bool equ(db i,db j) {
return fabs(j-i)<=eps;
}
inline db ask() {
int l=1,r=0;
for(int i=1;i<=m;i++) {
while(l<r&&Right(a[i],p[r-1])) r--;
while(l<r&&Right(a[i],p[l])) l++;
if(l>r) {
q[++r]=i;
continue;
}
if(!equ(a[i].ang,a[q[r]].ang)) q[++r]=i;
else if(Right(a[i],a[q[r]].s)) q[r]=i;
if(l<r) p[r-1]=Crs(a[q[r-1]],a[q[r]]);
}
while(l<r&&Right(a[q[l]],p[r-1])) r--;
p[r]=Crs(a[q[r]],a[q[l]]); db ret=INF;
Po t;
for(int i=l,j=1;i<=r&&j<=n;) {
if(p[i].x+eps<b[j].x) {
t=Crs((Li){b[j-1],b[j]},(Li){(Po){p[i].x,0},(Po){p[i].x,1}});
ret=min(ret,p[i].y-t.y);
i++;
} else if(equ(p[i].x,b[j].x)) {
ret=min(ret,p[i].y-b[j].y);
i++,j++;
} else {
t=Crs((Li){p[i-1],p[i]},(Li){(Po){b[j].x,0},(Po){b[j].x,1}});
ret=min(ret,t.y-b[j].y);
j++;
}
}
return ret;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%Lf",&b[i].x);
}
for(int i=1;i<=n;i++) {
scanf("%Lf",&b[i].y);
}
a[++m]=(Li){(Po){b[1].x,INF},b[1],atan2(-INF,0)};
a[++m]=(Li){b[n],(Po){b[n].x,INF},atan2(INF,0)};
a[++m]=(Li){(Po){b[n].x,INF},(Po){b[1].x,INF},atan2(0,b[1].x-b[n].x)};
for(int i=2;i<=n;i++) {
Po t=b[i]-b[i-1];
a[++m]=(Li){b[i-1],b[i],atan2(t.y,t.x)};
}
sort(a+1,a+m+1,cmp);
printf("%.3Lf\n",ask());
return 0;
}
例题4
[P4250 SCOI2015]小凸想跑步 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
猜测区域是多边形,然后推式子(见小本本)
分类讨论\(Ax+By+C<0\)的情况(使得答案(x,y)在左边)
#include<bits/stdc++.h>
#define db long double
const db eps=1e-9,INF=1e14;
using namespace std;
const int N=1e5+5;
int n,m,q[N];
struct Po{db x,y; }p[N],b[N];
inline db operator *(Po i,Po j) {
return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
return (Po){i.x*j,i.y*j};
}
inline Po operator /(Po i,db j) {
return (Po){i.x/j,i.y/j};
}
inline Po operator -(Po i,Po j) {
return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
return (Po){i.x+j.x,i.y+j.y};
}
struct Li{Po s,t; db ang; }a[N];
inline bool cmp(Li i,Li j) {
return i.ang<j.ang;
}
inline bool Right(Li i,Po j) {
return (i.t-i.s)*(j-i.s)<-eps;
}
inline Po Crs(Li i,Li j) {
Po A=i.s,B=i.t,C=j.s,D=j.t;
db s1=(A-C)*(D-C),s2=(D-C)*(B-C);
return A+(B-A)*(s1/(s1+s2));
}
inline bool equ(db i,db j) {
return fabs(j-i)<=eps;
}
inline db ask() {
int l=1,r=0;
for(int i=1;i<=m;i++) {
while(l<r&&Right(a[i],p[r-1])) r--;
while(l<r&&Right(a[i],p[l])) l++;
if(l>r) {
q[++r]=i;
continue;
}
if(!equ(a[i].ang,a[q[r]].ang)) q[++r]=i;
else if(Right(a[i],a[q[r]].s)) q[r]=i;
if(l<r) p[r-1]=Crs(a[q[r-1]],a[q[r]]);
}
while(l<r&&Right(a[q[l]],p[r-1])) r--;
p[r]=Crs(a[q[r]],a[q[l]]); db ret=p[r]*p[l];
for(int i=l+1;i<=r;i++) {
ret+=p[i-1]*p[i];
}
return ret;
}
inline Li Line(Po s,Po t) {
Po tmp=t-s;
return (Li){s,t,atan2(tmp.y,tmp.x)};
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%Lf%Lf",&b[i].x,&b[i].y);
}
db ans=b[n]*b[1];
for(int i=2;i<=n;i++) {
ans+=b[i-1]*b[i];
}
a[++m]=Line(b[1],b[2]);
db A,B,C,xa=b[1].x,xb=b[2].x,xc,xd,ya=b[1].y,yb=b[2].y,yc,yd;
for(int i=2,j=3;i<=n;i++,j=j%n+1) {
xc=b[i].x,yc=b[i].y,xd=b[j].x,yd=b[j].y;
A=-yb+ya-yc+yd,B=xb-xa+xc-xd,C=-xb*ya+xa*yb+xd*yc-xc*yd;
if(B<0) {
a[++m]=Line((Po){0,-C/B},(Po){1,(-A-C)/B});
} else if(B==0) {
if(A<0) a[++m]=Line((Po){-C/A,1},(Po){-C/A,0});
else if(A==0) {
if(C) {
puts("0.0000");
return 0;
}
} else {
a[++m]=Line((Po){-C/A,0},(Po){-C/A,1});
}
} else {
a[++m]=Line((Po){1,(-A-C)/B},(Po){0,-C/B});
}
}
sort(a+1,a+m+1,cmp);
printf("%.4Lf\n",ask()/ans);
return 0;
}
例题5
[P3256 [JLOI2013]赛车 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
WA了一次:没考虑到有赛车速度位置都相同
#include<bits/stdc++.h>
#define db long double
const db eps=1e-18,INF=1e18;
using namespace std;
const int N=1e4+5;
int n,m,q[N],k[N],v[N];
struct Po{db x,y; }p[N],b[N];
inline db operator *(Po i,Po j) {
return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
return (Po){i.x*j,i.y*j};
}
inline Po operator /(Po i,db j) {
return (Po){i.x/j,i.y/j};
}
inline Po operator -(Po i,Po j) {
return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
return (Po){i.x+j.x,i.y+j.y};
}
struct Li{Po s,t; db ang; int id; }a[N];
inline bool cmp(Li i,Li j) {
return i.ang<j.ang;
}
inline bool Right(Li i,Po j) {
return (i.t-i.s)*(j-i.s)<-eps;
}
inline Po Crs(Li i,Li j) {
Po A=i.s,B=i.t,C=j.s,D=j.t;
db s1=(A-C)*(D-C),s2=(D-C)*(B-C);
return A+(B-A)*(s1/(s1+s2));
}
inline bool equ(db i,db j) {
return fabs(j-i)<=eps;
}
bool fl[N];
inline void ask() {
int l=1,r=0;
for(int i=1;i<=m;i++) {
while(l<r&&Right(a[i],p[r-1])) r--;
while(l<r&&Right(a[i],p[l])) l++;
if(l>r) {
q[++r]=i;
continue;
}
if(!equ(a[i].ang,a[q[r]].ang)) q[++r]=i;
else if(Right(a[i],a[q[r]].s)) q[r]=i;
if(l<r) p[r-1]=Crs(a[q[r-1]],a[q[r]]);
}
while(l<r&&Right(a[q[l]],p[r-1])) r--;
set<int>S;
for(int i=l;i<=r;i++) {
if(a[q[i]].id) S.insert(a[q[i]].id),fl[a[q[i]].id]=1;
}
for(int i=2;i<=m;i++) {
if(a[i].ang==a[i-1].ang&&a[i].s.x==a[i-1].s.x&&a[i].s.y==a[i-1].s.y&&a[i].t.x==a[i-1].t.x&&a[i].t.y==a[i-1].t.y) {
if(fl[a[i-1].id]&&!fl[a[i].id]) fl[a[i].id]=1,S.insert(a[i].id);
}
}
printf("%d\n",(int)S.size());
for(int i:S) {
printf("%d ",i);
}
}
inline Li Line(Po s,Po t,int id) {
Po tmp=t-s;
return (Li){s,t,atan2(tmp.y,tmp.x),id};
}
int main() {
// freopen("1.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&k[i]);
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
a[++m]=Line((Po){0,INF},(Po){0,0},0);
a[++m]=Line((Po){INF,0},(Po){INF,INF},0);
a[++m]=Line((Po){INF,INF},(Po){0,INF},0);
for(int i=1;i<=n;i++) {
a[++m]=Line((Po){0,k[i]},(Po){1,k[i]+v[i]},i);
}
sort(a+1,a+m+1,cmp);
ask();
return 0;
}
例题6
F. 逃离监控 - 「计算几何」第4章 半平面交 - 金牌导航 - 课程 - YbtOJ
可以发现:每个亲戚的掌管范围是一个多边形(见小本本),所以可以将相邻的亲戚连起来(对每个亲戚求一遍半平面交),然后跑BFS即可
#include<bits/stdc++.h>
#define db long double
const db eps=1e-10;
using namespace std;
const int N=610;
int n,m,q[N];
struct Po{db x,y; }b[N],p[N];
struct Li{Po s,t; db ang; int id; }a[N];
vector<int>V[N];
inline Po operator -(Po i,Po j) {
return (Po){i.x-j.x,i.y-j.y};
}
inline Po operator +(Po i,Po j) {
return (Po){i.x+j.x,i.y+j.y};
}
inline Po operator *(Po i,db j) {
return (Po){i.x*j,i.y*j};
}
inline Li Line(Po s,Po t,int id) {
Po tmp=t-s;
return (Li){s,t,atan2(tmp.y,tmp.x),id};
}
inline db operator *(Po i,Po j) {
return i.x*j.y-i.y*j.x;
}
inline bool cmp(Li i,Li j) {
return i.ang<j.ang;
}
inline bool Right(Li i,Po j) {
return (i.t-i.s)*(j-i.s)<-eps;
}
inline bool equ(db x,db y) {
return fabs(x-y)<=eps;
}
inline Po Crs(Li i,Li j) {
Po A=i.s,B=i.t,C=j.s,D=j.t;
db s1=(A-C)*(D-C),s2=(D-C)*(B-C);
return A+(B-A)*(s1/(s1+s2));
}
bool fl[N];
void ask(int u) {
int l=1,r=0;
for(int i=1;i<=m;i++) {
while(l<r&&Right(a[i],p[r-1])) r--;
while(l<r&&Right(a[i],p[l])) l++;
if(l>r) {
q[++r]=i;
continue;
}
if(!equ(a[i].ang,a[q[r]].ang)) q[++r]=i;
else if(Right(a[i],a[q[r]].s)) q[r]=i;
if(l<r) p[r-1]=Crs(a[q[r]],a[q[r-1]]);
}
while(l<r&&Right(a[q[l]],p[r-1])) r--;
memset(fl,0,sizeof(fl));
for(int i=l;i<=r;i++) {
if(!fl[a[q[i]].id]) {
V[u].push_back(a[q[i]].id);
fl[a[q[i]].id]=1;
}
}
}
inline db dis(Po i) {
return sqrt(i.x*i.x+i.y*i.y);
}
int f[N];
int bfs(int s) {
memset(f,0,sizeof(f));
int l=1,r=1; q[1]=s,f[s]=1;
while(l<=r) {
int u=q[l]; l++;
for(int v:V[u]) {
if(v==0) return f[u];
if(!f[v]) {
f[v]=f[u]+1;
q[++r]=v;
}
}
}
}
int main() {
int T; scanf("%d",&T);
while(T--) {
db x0,x1,y0,Y1;
scanf("%d%Lf%Lf%Lf%Lf",&n,&x1,&Y1,&x0,&y0),m=0;
if(n==0) {
puts("0");
continue;
}
for(int i=1;i<=n;i++) {
scanf("%Lf%Lf",&b[i].x,&b[i].y);
}
for(int i=1;i<=n;i++) {
m=0;
a[++m]=Line((Po){0,Y1},(Po){0,0},0);
a[++m]=Line((Po){0,0},(Po){x1,0},0);
a[++m]=Line((Po){x1,0},(Po){x1,Y1},0);
a[++m]=Line((Po){x1,Y1},(Po){0,Y1},0);
for(int j=1;j<=n;j++) {
if(i!=j) {
db A=(b[j].x-b[i].x)*2,B=(b[j].y-b[i].y)*2,C=(b[i].y*b[i].y-b[j].y*b[j].y+b[i].x*b[i].x-b[j].x*b[j].x);
if(B<0) {
a[++m]=Line((Po){0,-C/B},(Po){1,(-C-A)/B},j);
} else if(B==0) {
if(A<0) {
a[++m]=Line((Po){-C/A,1},(Po){-C/A,0},j);
} else a[++m]=Line((Po){-C/A,0},(Po){-C/A,1},j);
} else {
a[++m]=Line((Po){1,(-C-A)/B},(Po){0,-C/B},j);
}
}
}
sort(a+1,a+m+1,cmp);
V[i].clear();
ask(i);
}
int num=1; Po tmp=(Po){b[num].x-x0,b[num].y-y0},tt;
for(int i=1;i<=n;i++) {
tt=(Po){b[i].x-x0,b[i].y-y0};
if(dis(tmp)>dis(tt)) {
num=i;
tmp=tt;
}
}
printf("%d\n",bfs(num));
}
return 0;
}

浙公网安备 33010602011771号