基础计算几何

Posted 斗奋力努

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础计算几何相关的知识,希望对你有一定的参考价值。

前言:匡斌这套基础计算几何挺仁慈的 ,感觉没有卡一些奇奇怪怪的东西,大家应该可以随便切,这里会提供所有题目的代码,以及提供部分的题思路。
省赛冲冲冲
主要目的是骗阅读量。

题目

TOYS

考查点: 叉积点积的基础运用

思路:

对于每组数据,输出 n+1行。

n个纸板将收纳盒划分为了 n+1 个分区,从左到右编号为 0∼n。

按照分区编号从小到大的顺序,每行输出一行信息 i: j,其中i表示分区编号,j 表示分区内玩具数量。

记录上下点,二分找答案。如果在左边,area为正。否则为负

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll,ll>PDD;
const double eps=1e-8;
const int N=5e3+5;
ll n,m,x1,y1,x2,y2;
PDD a[N],b[N]; //a表示上面点,b表示下面点
ll ans[N];    //答案

PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}

ll area(PDD p,PDD a,PDD b){return (a-p)*(b-p);}

ll find(ll x,ll y){
    ll l=0,r=n;
    while(l<r){
        ll mid=l+r>>1;
        if(area(b[mid],a[mid],{x,y})>0) r=mid;
        else l=mid+1;
    }
    return r;
}

int main(){
    int is_first=true;
    while(scanf("%lld",&n)){
        if(n==0) break;
        scanf("%lld%lld%lld%lld%lld",&m,&x1,&y1,&x2,&y2);
        for(ll i=0;i<n;i++){
            ll u,l;scanf("%lld%lld",&u,&l);
            a[i]={u,y1},b[i]={l,y2};
        }
        a[n]={x2,y1},b[n]={x2,y2};
        if(is_first) is_first=false;
        else printf("\\n");
        memset(ans,0,sizeof(ans));
        for(ll i=1;i<=m;i++){
            ll x,y; scanf("%lld%lld",&x,&y);
            ans[find(x,y)]++;
        }
        for(int i=0;i<=n;i++) printf("%d: %d\\n",i,ans[i]);
    }
}

ToyStorage

考查点: 叉积点积的基础运用

思路:跟上一条差不多,不过这题是无序的,要先将纸板从小到大排序

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll,ll>PDD;
const ll N=1e3+5;
const double eps=1e-8;
ll n,m,x1,yy,x2,y2;
ll ans[N],num[N];
PDD a[N],b[N];
struct node{
    ll sh,xi;
    bool operator<(const node &t)const{
        if(sh==t.sh) return xi<t.xi;
        return sh<t.sh;
    }
}c[N];

PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
ll area(PDD p,PDD a,PDD b){return (a-p)*(b-p);}

ll find(ll x,ll y){
    ll l=0,r=n;
    while(l<r){
        ll mid=(l+r)>>1;
        if(area(b[mid],a[mid],{x,y})>0) r=mid;
        else l=mid+1;
    }
    return r;
}

int main(){
    while(scanf("%lld",&n)){
        if(n==0) break;
        scanf("%lld%lld%lld%lld%lld",&m,&x1,&yy,&x2,&y2);
        for(ll i=0;i<n;i++) scanf("%lld%lld",&c[i].sh,&c[i].xi);
        c[n]={x2,y2};
        sort(c,c+n+1);
        for(ll i=0;i<=n;i++) {
            a[i]={c[i].sh,yy};
            b[i]={c[i].xi,y2};    
        }
        memset(ans,0,sizeof(ans));
        for(ll i=1;i<=m;i++){
            ll x,y;scanf("%lld%lld",&x,&y);
            ans[find(x,y)]++;
        }
        memset(num,0,sizeof(num));
        for(int i=0;i<=n;i++){
            if(ans[i]) num[ans[i]]++;
        }
        printf("Box\\n");
        for(ll i=0;i<=n;i++){
            if(num[i]) printf("%lld: %lld\\n",i,num[i]);
        }
    }
}

Segments

题意:t组数据,每组有n条线段,问是否存在某直线穿过所有线段

思路:根据题意,我们可以知道,将线段的所有端点暴力枚举,每次选两个,构成直线。

​ 然后再去跟所有线段判断是否存在交点即可。可以画图去枚举两点构成直线,就可以发现规律

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=205;
const double eps=1e-8;
int n;
PDD q[N],a[N],b[N];

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

int cmp(double x,double y){
    if(fabs(x-y)<eps) return 0;
    if(x<y) return -1;
    return 1;
}

PDD  operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
double area(PDD p,PDD a,PDD b){return (a-p)*(b-p);}

bool check(){
    for(int i=0;i<2*n;i++){  //枚举两个端点
        for(int j=i+1;j<2*n;j++){
            if(!cmp(q[i].x,q[j].x)&&!cmp(q[i].y,q[j].y)) continue;  //不能是同一点
            bool flag=true;
            for(int k=0;k<n;k++){  //枚举线段
                if(sign(area(q[i],q[j],a[k]))*sign(area(q[i],q[j],b[k]))>0){//>0即代表无端点
                    flag=false;break;
                }
            }
            if(flag) return true;
        }
    }
    return false;
}

void solve(){
    scanf("%d",&n);
    for(int i=0,k=0;i<n;i++){
        double x1,y1,x2,y2;
        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
        q[k++]={x1,y1},q[k++]={x2,y2};  //记录所有端点
        a[i]={x1,y1},b[i]={x2,y2};
    }
    if(check()) puts("Yes!");
    else puts("No!");
}

int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
}

Intersecting-Lines

考察点:两直线的位置关系

题意:两直线是相交、相合还是平行

​ 相交输出:交点坐标;

​ 相合输出:LINE

​ 平行输出:NONE

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const double eps=1e-8;
PDD a[10];
int n;

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    else return 1;
}

PDD operator+(PDD a,PDD b){return {a.x+b.x,a.y+b.y};}
PDD operator*(PDD a,double b){return {a.x*b,a.y*b};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
double area(PDD p,PDD a,PDD b){return (a-p)*(b-p);}

PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w){//求交点,点项式表示
    PDD u=p-q;
    double t=(w*u)/(v*w);
    return p+v*t;
}

int main(){
    scanf("%d",&n);
    puts("INTERSECTING LINES OUTPUT");
    for(int i=1;i<=n;i++){
        for(int j=1;j<=4;j++) scanf("%lf%lf",&a[j].x,&a[j].y);
        a[5]=a[1]-a[2]; //向量(a-->d)
        a[6]=a[3]-a[4]; //向量(c-->d)
        if(sign(a[5]*a[6])==0){
            if(area(a[1],a[3],a[4])==0) puts("LINE");  //相合
            else puts("NONE"); //平行
        }
        else{
            PDD t=get_line_intersection(a[1],a[5],a[3],a[6]); //求交点
            printf("POINT %.2f %.2f\\n",t.x,t.y);
        }
    }
    puts("END OF OUTPUT");
    return 0;
}

TheDoors

考察点:最短路+计算几何+思维

题意:起点(0,5),终点(10,5)的10*10的房间,房间中存在一些墙,问从起点到终点的最短距离

注:poj年纪有点大了,不用用优先队列,理论来说本题最短路部分是可以用邻接表写的。

​ 不过poj不允许,这里会提供一份用邻接矩阵写的ac代码和一份用邻接表写的tle代码(其他oj应该可以过)

ac代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=406;
const double eps=1e-8;
const double inf=0x3f3f3f3f;
int n,po,li;
PDD point[N];
double dis[N];
bool vis[N];
int idx;
double mp[N][N];
struct Line{
    PDD a,b;
}line[N];

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

PDD operator-(PDD a,PDD b){return{a.x-b.x,a.y-b.y};}
PDD operator*(PDD a,double b){return{a.x*b,a.y*b};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
double get_dis(int a,int b){//两点构成线段的长度
    double xx=point[a].x-point[b].x;
    double yy=point[a].y-point[b].y;
    return sqrt(xx*xx+yy*yy);
}

bool segment_intersection(PDD a1,PDD a2,PDD b1,PDD b2){ //两线段是否有交点
    double c1=(a1-a2)*(a1-b1);
    double c2=(a1-a2)*(a1-b2);
    double c3=(b1-b2)*(b1-a2);
    double c4=(b1-b2)*(b1-a1);
    return sign(c1)*sign(c2)<0&&sign(c3)*sign(c4)<0;
}

bool check(int x,int y){
    for(int i=1;i<=li;i++){//拿所有的墙去与可能的路线比较,看是否存在交点,存在则不能成为边,因为不能穿墙
        if(segment_intersection(point[x],point[y],line[i].a,line[i].b)){
            return false;
        }
    }
    return true;
}

void get_could_path(){  //获得所有可行路线,存在邻接矩阵中
    for(int i=1;i<=po+1;i++){
        for(int j=i;j<=po+1;j++){ 
            if(i==j) mp[i][j]=0;
            else mp[i][j]=mp[j][i]=inf;
        }
    }
    for(int i=1;i<=po;i++){
        for(int j=i+1;j<=po;j++){//枚举所有两个端点组合,看是否为可行路线
            if(check(i,j)){//可以就更新邻接矩阵
                double dist=get_dis(i,j);
                mp[i][j]=mp[j][i]=dist;
            }
        }
    }
}

void dijkstra(){ //最短路模板
    for(int i=1;i<=po;i++){
        dis[i]=mp[1][i];
        vis[i]=0;
    }
    dis[1]=0,vis[1]=1;
    int k;
    for(int i=2;i<=po;i++){
        double minn=inf;
        for(int j=1;j<=po;j++){
            if(!vis[j]&&dis[j]<minn){
                minn=dis[j];
                k=j;
            }
        }
        dis[k]=minn;
        vis[k]=1;
        for(int j=1;j<=po;j++){
            if(!vis[j]&&mp[k][j]+dis[k]<dis[j]){
                dis[j]=mp[k][j]+dis[k];
            }
        }
    }
}

int main(){
    while(scanf("%d",&n)){
        if(n==-1) break;
        po=li=0;
        point[++po]={0.0,5.0};
        for(int i=1;i<=n;i++){
            double x,y[7];
            y[0]=0.0,y[5]=10.0;  //起点
            scanf("%lf%lf%lf%lf%lf",&x,&y[1],&y[2],&y[3],&y[4]);
            for(int j=1;j<=4;j++) point[++po]={x,y[j]}; //获得所有端点
            line[++li]={{x,y[1]},{x,y[0]}};//获得所有的墙
            line[++li]={{x,y[3]},{x,y[2]}};
            line[++li]={{x,y[5]},{x,y[4]}};
        }
        point[++po]={10.0,5.0};//终点
        get_could_path();
        dijkstra();
        printf("%.2f\\n",dis[po]);
    }
}

tle的链式前

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=406;
const double eps=1e-8;
int n,po,li;
PDD point[N];
double dis[N];
bool vis[N];
int to[N*2],ne[N*2],fir[N],idx;
double val[N*2];
double mp[N][N];
struct Line{
    PDD a,b;
}line[N];

struct node{
    int u;
    double dis;
    bool operator <(const node &x)const{
        return dis>x.dis;
    }
};

void init(){
    po=li=idx=0;
    memset(fir,-1,sizeof(fir));
}

void add(int u,int v,double w){
    to[++idx]=v,val[idx]=w,ne[idx]=fir[u],fir[u]=idx;
}

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

PDD operator-(PDD a,PDD b){return{a.x-b.x,a.y-b.y};}
PDD operator*(PDD a,double b){return{a.x*b,a.y*b};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
double get_dis(int a,int b){
    double xx=point[a].x-point[b].x;
    double yy=point[a].y-point[b].y;
    return sqrt(xx*xx+yy*yy);
}

bool segment_intersection(PDD a1,PDD a2,PDD b1,PDD b2){ //两线段是否有交点
    double c1=(a1-a2)*(a1-b1);
    double c2=(a1-a2)*(a1-b2);
    double c3=(b1-b2)*(b1-a2);
    double c4=(b1-b2)*(b1-a1);
    return sign(c1)*sign(c2)<0&&sign(c3)*sign(c4)<0;
}

bool check(int x,int y){
    for(int i=1;i<=li;i++){
        if(segment_intersection(point[x],point[y],line[i].a,line[i].b)){
            return false;
        }
    }
    return true;
}

void get_could_path(){
    for(int i=1;i<=po;i++){
        for(int j=i+1;j<=po;j++){
            if(check(i,j)){
                double dist=get_dis(i,j);
                add(i,j,dist);
            }
        }
    }
}

void dijkstra(){
    for(int i=1;i<=po;i++){
        dis[i]=0x3f3f3f3f;
        vis[i]=0;
    }
    priority_queue<node>q;
    q.push(node{1,0});
    dis[1]=0;
    while(!q.empty()){
        node t=q.top(); q.pop();
        if(vis[t.u]) continue;
        vis[t.u]=1;
        for(int i=fir[t.u];~i;i=ne[i]){
            int v=to[i];
            if(!vis[v]&&dis[v]>dis[t.u]+val[i]){
                dis[v]=dis[t.u]+val[i];
                q.push(node{v,dis[v]});
            }
        }
    }
}

int main(){
    while(scanf("%d",&n)){
        if(n==-1) break;
        init();
        point[++po]={0.0,5.0};
        for(int i=1;i<=n;i++){
            double x,y[7];
            y[0]=0.0,y[5]=10.0;
            scanf("%lf%lf%lf%lf%lf",&x,&y[1],&y[2],&y[3],&y[4]);
            for(int j=1;j<=4;j++) point[++po]={x,y[j]};
            line[++li]={{x,y[1]},{x,y[0]}};
            line[++li]={{x,y[3]},{x,y[2]}};
            line[++li]={{x,y[5]},{x,y[4]}};
        }
        point[++po]={10.0,5.0};
        get_could_path();
        dijkstra();
        printf("%.2f\\n",dis[po]);
    }
}

Pick-upsticks

题意:

有n根棍子,从1到n依次放到平面上,当前放置的棍子i如果与已经存在的棍子k有交点,那么棍子k将不做贡献

思路:

即某根棍子j如果其他棍子压了一次,就不会做贡献。要求输出所有没有被其他棍子的顶点棍子

队列优化,加入后,去与还没有被压过的棍子判断是否存在交点,每次都pop一次,有就继续,没有就在push进去。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=1e5+5;
const double eps=1e-8;
int n;
PDD a[N][2];
bool vis[N];
queue<int>q;

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    else return 1;
}

PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
PDD operator+(PDD a,PDD b){return {a.x+b.x,a.y+b.y};}
PDD operator*(PDD a,double b){return {a.x*b,a.y*b};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
double area(PDD p,PDD a,PDD b){return (a-p)*(b-p);}

bool segment_intersection(PDD a1,PDD a2,PDD b1,PDD b2){ //两线段是否有交点
    double c1=(a1-a2)*(a1-b1);
    double c2=(a1-a2)*(a1-b2);
    double c3=(b1-b2)*(b1-a2);
    double c4=(b1-b2)*(b1-a1);
    return sign(c1)*sign(c2)<=0&&sign(c3)*sign(c4)<=0;
}

int main(){
    while(scanf("%d",&n),n){
        for(int j=0;j<=1;j++) scanf("%lf%lf",&a[1][j].x,&a[1][j].y);
        q.push(1);
        for(int i=2;i<=n;i++){
            for(int j=0;j<=1;j++) scanf("%lf%lf",&a[i][j].x,&a[i][j].y);
            q.push(i);
            while(!q.empty()){
                int k=q.front(); q.pop();
                if(k==i){q.push(k);break;}
                bool flag=segment_intersection(a[k][0],a[k][1],a[i][0],a[i][1]);
                if(!flag) q.push(k);
            }
        }
        printf("Top sticks: %d",q.front());
        q.pop();
        while(!q.empty()){
            printf(", %d",q.front());
            q.pop();
        }
        printf(".\\n",n);
    }
}

TreasureHunt

题意:

在100*100的正方形中有一些墙,同时还有一个点在正方形内。

问该点离开正方形最少穿过多少墙

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=105;
const double eps=1e-8;
int n,mi;
PDD p[N];
struct Line{
    PDD a,b;
}line[N];

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

PDD operator-(PDD a,PDD b){return{a.x-b.x,a.y-b.y};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}

bool segment_intersection(PDD a1,PDD a2,PDD b1,PDD b2){ //两线段是否有交点
    double c1=(a1-a2)*(a1-b1);
    double c2=(a1-a2)*(a1-b2);
    double c3=(b1-b2)*(b1-a2);
    double c4=(b1-b2)*(b1-a1);
    return sign(c1)*sign(c2)<0&&sign(c3)*sign(c4)<0;
}

int solve(PDD a,PDD b){
    int sum=0;
    for(int i=1;i<=n;i++){
        if(segment_intersection(a,b,line[i].a,line[i].b)) sum++;
    }
    return sum;
}

int main(){
    while(scanf("%d",&n)!=EOF){
        mi=n;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&line[i].a.x,&line[i].a.y,&line[i].b.x,&line[i].b.y);
        }
        scanf("%lf%lf",&p[0].x,&p[0].y);
        p[1]={0.0,0.0},p[2]={0.0,0.0},p[3]={100.0,0.0},p[4]={0.0,100.0};//枚举下边界,左边界,右边界,上边界的起点
        for(int i=1;i<=100;i++){ //枚举边界上面的所有点
            p[1].x=p[2].y=p[3].y=p[4].x=i;
            mi=min(mi,solve(p[0],p[1])); //求起点与下边界构成线段与所有线段的交点数
            mi=min(mi,solve(p[0],p[2])); //求起点与下边界构成线段与所有线段的交点数
            mi=min(mi,solve(p[0],p[3])); //求起点与下边界构成线段与所有线段的交点数
            mi=min(mi,solve(p[0],p[4])); //求起点与下边界构成线段与所有线段的交点数
        }
        printf("Number of doors = %d\\n",mi+1); 
    }
    return 0;
}

Intersection

题意:如果线段和矩形相交或线段在矩形内部就是输出T,否则F。

坑人题,没必要写

#include<iostream>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define pi acos(-1)
#define ll long long
#define mod 1000000007

using namespace std;

const double eps=1e-8;
const int N=1005,maxn=100005,inf=0x3f3f3f3f;

struct point{
    int x,y;
};
struct line{
   point a,b;
}l[N];

bool cek(point p,point a,point b){
    return (p.x>=a.x&&p.x<=b.x)&&(p.y>=a.y&&p.y<=b.y);
}

int mul(point p,point u,point v)
{
    return (u.x-v.x)*(p.y-u.y)-(u.y-v.y)*(p.x-u.x);
}
bool acoss(line u,line v)
{
    if(mul(u.a,v.a,v.b)*mul(u.b,v.a,v.b)<0&&mul(v.a,u.a,u.b)*mul(v.b,u.a,u.b)<0)return 1;
    if(mul(u.a,v.a,v.b)==0&&(u.a.x-v.a.x)*(u.a.x-v.b.x)<=0&&(u.a.y-v.a.y)*(u.a.y-v.b.y)<=0)return 1;
    if(mul(u.b,v.a,v.b)==0&&(u.b.x-v.a.x)*(u.b.x-v.b.x)<=0&&(u.b.y-v.a.y)*(u.b.y-v.b.y)<=0)return 1;
    if(mul(v.a,u.a,u.b)==0&&(v.a.x-u.a.x)*(v.a.x-u.b.x)<=0&&(v.a.y-u.a.y)*(v.a.y-u.b.y)<=0)return 1;
    if(mul(v.b,u.a,u.b)==0&&(v.b.x-u.a.x)*(v.b.x-u.b.x)<=0&&(v.b.y-u.a.y)*(v.b.y-u.b.y)<=0)return 1;
    return 0;
}
int main()
{
    int n;
    cin>>n;
    while(n--){
        int x1,y1,x2,y2;
        cin>>l[0].a.x>>l[0].a.y>>l[0].b.x>>l[0].b.y>>x1>>y1>>x2>>y2;
        if(x1>x2)swap(x1,x2);
        if(y1>y2)swap(y1,y2);
        if(cek(l[0].a,{x1,y1},{x2,y2})&&cek(l[0].b,{x1,y1},{x2,y2}))
        {
            cout<<"T"<<endl;
            continue;
        }
        l[1].a={x1,y1},l[1].b={x2,y1};
        l[2].a={x1,y1},l[2].b={x1,y2};
        l[3].a={x1,y2},l[3].b={x2,y2};
        l[4].a={x2,y1},l[4].b={x2,y2};
        int flag=0;
        for(int i=1;i<=4;i++)
            if(acoss(l[0],l[i]))
               flag=1;
        if(flag)cout<<"T"<<endl;
        else cout<<"F"<<endl;
    }
    return 0;
}

SpaceAnt

小广告:本题详解

KadjSquares

考察点:思维

题意:45°依次放置正方形自上而下可以的正方形编号

队友用一个奇怪的二分写过去了qwq

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=105;
int n;
int s[N],l[N],r[N];

int main(){
    while(scanf("%d",&n),n){
        for(int i=1;i<=n;i++) s[i]=l[i]=r[i]=0;
        for(int i=1;i<=n;i++) scanf("%d",&s[i]);
        for(int i=1;i<=n;i++){
            for(int j=1;j<i;j++){
                l[i]=max(l[i],r[j]-abs(s[i]-s[j])); //求出左端点
            }
            r[i]=l[i]+2*s[i]; //根据左端点算出右端点,距离扩大了根号2倍
        }
        // for(int i=1;i<=n;i++) cout<<l[i]<<"  "<<r[i]<<"\\n";
        for(int i=1;i<=n;i++){
            for(int j=1;j<i;j++){
                if(s[i]<s[j]&&l[i]<r[j]) l[i]=r[j];//i有部分被j遮挡了
                else if(s[i]>s[j]&&l[i]<r[j]) r[j]=l[i];//i有部分遮挡了j
            }
        }
        for(int i=1;i<=n;i++){
            if(l[i]<r[i]) printf("%d ",i);//存在未被遮挡部分
        }
        puts("");
    }
    return 0;
}

AnEasyProblem?!

小广告:本题详解

Pipe

题意:一个宽度为1的不规则管道,管道壁不透光不反光,求从管道一头射入一束光线,光线在管道内沿直线传播最远能传播多远(横坐标能到达的最大值)。每次用例输出一行,若光线能穿透整个管道,输出Through all the pipe.否则输出能到达的x值,保留两位小数。

思路:直接枚举上下两两折点就行了,然后去判断为管道的相交情况,判断能否穿透,不能就最后取max

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const double eps=1e-8;
const int inf=0x3f3f3f3f;
const int N=25;
int n;
PDD up[N],down[N];

PDD operator+(PDD a,PDD b){return {a.x+b.x,a.y+b.y};}
PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
PDD operator*(PDD a,double b){return {a.x*b,a.y*b};}
PDD operator/(PDD a,double b){return {a.x/b,a.y/b};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
double operator&(PDD a,PDD b){return a.x*b.x+a.y*b.y;}

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

bool segment_intersection(PDD a1,PDD a2,PDD b1,PDD b2){ 
    double c1=(a1-a2)*(a1-b1);
    double c2=(a1-a2)*(a1-b2);
    return sign(c1)*sign(c2)<=0;
}

PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w){//两直线的交点(点项式)
    PDD u=p-q;
    double t=(w*u)/(v*w);
    return p+v*t;
}
void solve(){
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&up[i].x,&up[i].y);
        down[i].x=up[i].x,down[i].y=up[i].y-1;
    }
    int flag=false,k;
    double ans=-inf;
    for(int i=1;i<=n&&!flag;i++){
        for(int j=1;j<=n&&!flag;j++){
            if(i!=j){
                for(k=1;k<=n;k++){
                    if(!segment_intersection(up[i],down[j],up[k],down[k])) break;
                }
                if(k==n+1) flag=true;
                else if(k>max(i,j)){
                    PDD tmp=get_line_intersection(up[i],down[j]-up[i],up[k-1],up[k]-up[k-1]);
                    ans=max(ans,tmp.x);
                    tmp=get_line_intersection(up[i],down[j]-up[i],down[k-1],down[k]-down[k-1]);
                    ans=max(ans,tmp.x);
                }
            }
        }
    }
    if(flag) puts("Through all the pipe.");
    else printf("%.2f\\n",ans);
}

int main(){
    while(scanf("%d",&n)){
        if(n==0) break;
        solve();
    }
    return 0;
}

GeometricShapes

题意:n个图形,输出每个图形与其他图形的相交情况

图形种类:正方形、长方形、直线、多边形、三角形。正方形和长方形只给出一条对角线上的两点,所以不一定水平

思路:其实挺简单的,就是存好每个图形的每个点,和记好标号id,每次去暴力判断就行了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const double eps=1e-8;
const int N=30;
char s[N],s1[N],s2[N];
vector<int>vid,ve[N];

struct node{
    int num;
    PDD p[N];
}a[N];

PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0)return -1;
    return 1;
}

bool segment_intersection(PDD a1,PDD a2,PDD b1,PDD b2){ //两线段是否有交点
    double c1=(a1-a2)*(a1-b1);
    double c2=(a1-a2)*(a1-b2);
    double c3=(b1-b2)*(b1-a2);
    double c4=(b1-b2)*(b1-a1);
    return sign(c1)*sign(c2)<=0&&sign(c3)*sign(c4)<=0;
}

void init(){
    for(int i=1;i<=26;i++){
        a[i].num=0;
        ve[i].clear();
    }
    vid.clear();
}

void add_polygon(int x){
    int id=s[0]-'A'+1;
    vid.push_back(id);
    a[id].num=x;
    for(int i=1;i<=x;i++) scanf(" (%lf,%lf)",&a[id].p[i].x,&a[id].p[i].y);
    a[id].p[x+1]=a[id].p[1];
}

void add_rectangle(){
    int id=s[0]-'A'+1;
    vid.push_back(id);
    a[id].num=4;
    for(int i=1;i<=3;i++) scanf(" (%lf,%lf)",&a[id].p[i].x,&a[id].p[i].y);
    a[id].p[4].x=a[id].p[1].x+a[id].p[3].x-a[id].p[2].x;
    a[id].p[4].y=a[id].p[1].y+a[id].p[3].y-a[id].p[2].y;
    a[id].p[5]=a[id].p[1];
}

void add_square(){
    int id=s[0]-'A'+1;
    vid.push_back(id);
    a[id].num=4;
    scanf(" (%lf,%lf)",&a[id].p[1].x,&a[id].p[1].y);
    scanf(" (%lf,%lf)",&a[id].p[3].x,&a[id].p[3].y);
    PDD mid;//找出剩下两个端点
    mid.x=(a[id].p[1].x+a[id].p[3].x)/2;
    mid.y=(a[id].p[1].y+a[id].p[3].y)/2;  
    a[id].p[2].x=mid.x-(a[id].p[1].y-mid.y),a[id].p[2].y=mid.y+(a[id].p[1].x-mid.x);
    a[id].p[4].x=mid.x+(a[id].p[1].y-mid.y),a[id].p[4].y=mid.y-(a[id].p[1].x-mid.x);
    a[id].p[5]=a[id].p[1];//起点重复,这样可以不用取模进行操作
    // for(int i=1;i<=4;i++) cout<<a[id].p[i].x<<"  "<<a[id].p[i].y<<"\\n";
}

void gao(int l,int r){
    for(int i=1;i<=a[l].num;i++){
        for(int j=1;j<=a[r].num;j++){//暴力判断两图形是否相交
            if(segment_intersection(a[l].p[i],a[l].p[i+1],a[r].p[j],a[r].p[j+1])){
                ve[l].push_back(r);//相交就存到答案ve,并直接结束
                return;
            }
        }
    }
}

void out1(){//输出
    int hv=vid.size();
    for(int k=0;k<hv;k++){
        int i=vid[k];
        int sz=ve[i].size();
        if(sz==0) printf("%c has no intersections\\n",i+'A'-1);
        else{
            sort(ve[i].begin(),ve[i].end());
            printf("%c intersects with ",i+'A'-1);
            if(sz==1) printf("%c\\n",ve[i][0]+'A'-1);
            else if(sz==2) printf("%c and %c\\n",ve[i][0]+'A'-1,ve[i][1]+'A'-1);
            else{
                for(int j=0;j<sz-1;j++) printf("%c, ",ve[i][j]+'A'-1);
                printf("and %c\\n",ve[i][sz-1]+'A'-1);
            }
        }
    }
    printf("\\n");
}

void solve(){
    sort(vid.begin(),vid.end());  //出现id排序
    int hv=vid.size();
    for(int i=0;i<hv;i++){
        for(int j=0;j<hv;j++){  //枚举每两个图形
            if(i!=j) gao(vid[i],vid[j]);
        }
    }
    out1();
}

int main(){
    while(scanf("%s",s)){
        if(s[0]=='-') {solve();init();continue;}
        else if(s[0]=='.') return 0;
        else{
            scanf("%s",s1);
            if(s1[0]=='l') add_polygon(2);  //直线
            else if(s1[0]=='t') add_polygon(3); //三角形
            else if(s1[0]=='r') add_rectangle();//长方形
            else if(s1[0]=='s') add_square();//正方形
            else{
                int x;scanf("%d",&x);
                add_polygon(x);//多边形
            }
        }
    }
    return 0;
}

A-Round-Pegin-a-Ground-Hole

题意:

输入n个点,圆的半径以及圆的坐标X,Y。然后是每个点的x,y坐标.

判断输入的点是不是能够组成凸包,不能的话输出 HOLE IS ILL-FORMED,

判断圆是否在凸包内.如果在的话输出PEG WILL FIT.

否则输出 PEG WILL NOT FIT。

板子题,存一个好板子就可以过,hdu里面应该是数据水了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=1005;
const double eps=1e-8;
int n;
int stk[N],top;
bool used[N];
PDD q[N];

struct Circle{
    PDD o;
    double r;
}cir;

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

PDD operator-(PDD a,PDD b){return {a.x-b.x,a.y-b.y};}
double operator*(PDD a,PDD b){return a.x*b.y-a.y*b.x;}
double operator&(PDD a,PDD b){return a.x*b.x+a.y*b.y;}
double area(PDD p,PDD a,PDD b){return (a-p)*(b-p);}

double get_dis(PDD a,PDD b){
    double xx=a.x-b.x;
    double yy=a.y-b.y;
    return sqrt(xx*xx+yy*yy);
}

int sign_area_dir(double x){
    if(x<0) return 1;
    return 0;
}

double get_dis(PDD a,PDD b,PDD c){
    if(get_dis(a,b)<eps) return get_dis(b,c);
    if(((b-a)&(c-a))<-eps) return get_dis(a,c);
    if(((a-b)&(c-b))<-eps) return get_dis(b,c);
    return fabs((b-a)*(c-a))/get_dis(a,b);
}


bool is_convex(){
    q[n++]=q[0];
    q[n++]=q[1];
    int dir=sign_area_dir(area(q[0],q[1],q[2]));
    for(int i=3;i<n;i++){
        if(sign_area_dir(area(q[i-2],q[i-1],q[i]))!=dir) return false;
    }
    return true;
}

bool Point_in_convex(int n,double x,double y){
    int i,j=n-1;
    bool oddNodes=0;
    for(i=0;i<n;i++){
        if((q[i].y<y&&q[j].y>=y||q[j].y<y&&q[i].y>=y)&&(q[i].x<=x||q[j].x<=x)){
            oddNodes^=(q[i].x+(y-q[i].y)/(q[j].y-q[i].y)*(q[j].x-q[i].x)<x);
        }
        j=i;
    }
    return oddNodes;
}

bool Circle_in_convex(double x,double y,double r){
    PDD po;
    po={x,y};
    q[n]=q[0];
    for(int i=0;i<n;i++){
        if(get_dis(q[i],q[i+1],po)<r) return false;
    }
    return true;
}

void solve(){
    scanf("%lf%lf%lf",&cir.r,&cir.o.x,&cir.o.y);
    for(int i=0;i<n;i++) scanf("%lf%lf",&q[i].x,&q[i].y);
    if(!is_convex()) puts("HOLE IS ILL-FORMED");
    else if(!Point_in_convex(n,cir.o.x,cir.o.y)||!Circle_in_convex(cir.o.x,cir.o.y,cir.r))puts("PEG WILL NOT FIT");
    else puts("PEG WILL FIT");
}

int main(){
    while(scanf("%d",&n)){
        if(n<3) return 0;
        solve();
    }
}


Line-of-Sight

题意:

输入:多组数据。

每组数据:第一行x1,x2,y分别为房子的左端点的x坐标和右端点的x坐标以及y值

​ 第二行x1,x2,y分别为马路的左端点的x坐标和右端点的x坐标以及y值

​ 输入n,代表n个障碍物。随后n行每行x1,x2,y,分别为障碍物的左端点的x坐标和右端点的x坐标以及y值

问:输出马路上最长的连续区间,满足区间内每个点都可以完全看到房子

思路:感觉这个题计算几何体现的不太明显,应该算是一个小思维吧。

(口胡一下:解题时借助了区间合并的思维去解决重复遮挡的情况)

步骤1:我们知道有些障碍物是不会影响到马路上人的视野的,我们可以在读入的时候直接处理掉这些无影响障碍物。特判掉0的情况

步骤2:有影响障碍物按着(第一要素左端点x坐标,第二要素右端点x坐标排序)

步骤3:先处理最前面那个可能的单独区间,处理中间部分,最后处理最后面那个可能的单独区间

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef pair<double,double>PDD;
const int N=1005;
const double inf=1e9;
const double eps=1e-8;
int n,tot;
struct Line{
    double sx,ex,y;
}line[N];
Line house,road,temp;

int sign(double x){
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}

bool cek(Line te){return sign(te.y-house.y)>=0||sign(te.y-road.y)<=0;}

void get_in(Line a,Line b){//house的左右端点分别和每条障碍物的右左端点所构直线与road的交点
    Line te;
    te.sx=((b.sx*a.y-b.y*a.ex)+road.y*(a.ex-b.sx))/(a.y-b.y);//house右端点和该障碍物左端点构成直线与road的交点的x值
    te.ex=((b.ex*a.y-b.y*a.sx)+road.y*(a.sx-b.ex))/(a.y-b.y);//house左端点和该障碍物右端点构成直线与road的交点的x值
    te.y=road.y;
    if(te.sx>te.ex) swap(te.sx,te.ex);
    line[tot++]=te;
}

bool cmp(Line a,Line b){
    if(a.sx==b.sx) return a.ex<b.ex;
    return a.sx<b.sx;
}

int main(){
    while(scanf("%lf%lf%lf",&house.sx,&house.ex,&house.y)){//读入house
        if(!house.sx&&!house.ex&&!house.y) return 0;
        scanf("%lf%lf%lf",&road.sx,&road.ex,&road.y); //读入road
        scanf("%d",&n); tot=0;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf",&temp.sx,&temp.ex,&temp.y); //读入障碍物
            if(cek(temp)) continue; //步骤1(障碍物没得影响,不用加入)
            get_in(house,temp);
        }
        if(tot==0){printf("%.2f\\n",road.ex-road.sx);continue;}//特判没有有影响障碍物的情况
        sort(line,line+tot,cmp); //排序
        double max_len=-inf,r=line[0].ex,l=line[0].sx;
        if(l>road.sx){ //处理最前面可能的单独区间
            if(l<=road.ex) max_len=max(max_len,l-road.sx);
            else max_len=max(max_len,road.ex-road.sx);
        }
        for(int i=1;i<tot;i++){
            if(line[i].sx<=r) r=max(r,line[i].ex);
            else{
                max_len=max(max_len,line[i].sx-r);
                r=max(r,line[i].ex);
            }
        }
        if(r<road.ex) max_len=max(max_len,road.ex-r); //处理最后面可能的单独区间
        if(max_len==-inf) puts("No View");
        else printf("%.2f\\n",max_len);
    }
}

完结撒花

以上是关于基础计算几何的主要内容,如果未能解决你的问题,请参考以下文章

计算几何基础知识--求两个线段的交点

计算几何基础知识--求两个线段的交点

计算几何基础知识--求两个线段的交点

计算几何基础知识--求两个线段的交点

计算几何及其应用——计算几何基础

kuangbin专题计算几何基础(极角相关)