计算几何入门题之点,线,面,形基本关系以及点积叉积的理解

来源:互联网 发布:2017淘宝小类目 编辑:程序博客网 时间:2024/06/11 20:27

本人菜鸟一只,暑期做了一点计算几何的题目,现先将我之前转载的《计算几何题目推荐》的入门题的第一部分的解答与大家分享。

本篇所涉的题主要是与点,线,面,形基本关系以及点积叉积的理解相关的15道题,相对还是很基础的。但我这弱菜做的不太轻松。


POJ  2318 TOYS
题意:判断箱子每个块内有多少个玩具
分析:对于每个玩具的坐标,采用二分查找以确定玩具所在的块,其中玩具与分界(线段)的位置关系的判断可通过叉积来实现。
代码:

#include <stdio.h>#include <string.h>#define MAX 5010struct Point{int x,y;};struct Line{Point p1,p2;}line[MAX];int m,n,x1,y1,x2,y2;int a[MAX];Line makeline(int x1,int y1,int x2,int y2){Line L;L.p1.x=x1;L.p1.y=y1;L.p2.x=x2;L.p2.y=y2;return L;}int direction(Point p1,Point p0,Point p2){return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);}bool isleft(Line l,Point p){if(direction(l.p1,l.p2,p)>0)return true;else return false;}int binary_search(Point p)//二分查找{int low,high,mid;low=0;high=n;while(low<=high){mid=(low+high)/2;if(isleft(line[mid],p))high=mid-1;else low=mid+1;}return high;}int main(){int xx1,xx2,i,j;while(scanf("%d",&n)!=EOF&&n){scanf("%d%d%d%d%d",&m,&x1,&y1,&x2,&y2);memset(a,0,sizeof(a));line[0]=makeline(x1,y1,x1,y2);for(i=1;i<=n;i++){scanf("%d%d",&xx1,&xx2);line[i]=makeline(xx1,y1,xx2,y2);}line[n+1]=makeline(x2,y1,x2,y2);Point p;for(i=1;i<=m;i++){scanf("%d%d",&p.x,&p.y);a[binary_search(p)]++;}for(i=0;i<=n;i++){printf("%d: %d\n",i,a[i]);}printf("\n");}return 0;}


POJ 2398 Toy Storage
题意:与上一题一样,只是输出不同
代码:

#include <stdio.h>#include <vector>#include<algorithm>#include <string.h>using namespace std;#define MAX 5010struct Point{int x,y;};struct Line{Point p1,p2;}line[MAX];struct Count{int data,num;}co;int m,n,x1,y1,x2,y2;int a[MAX],b[MAX];bool Comp(Count c1,Count c2){return c1.num>c2.num;}bool Comp1(Line l1,Line l2){return l1.p1.x<l2.p1.x;}Line makeline(int x1,int y1,int x2,int y2){Line L;L.p1.x=x1;L.p1.y=y1;L.p2.x=x2;L.p2.y=y2;return L;}int direction(Point p1,Point p0,Point p2){return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);}bool isleft(Line l,Point p){if(direction(l.p1,l.p2,p)>0)return true;else return false;}int binary_search(Point p)//二分查找{int low,high,mid;low=0;high=n;while(low<=high){mid=(low+high)/2;if(isleft(line[mid],p))high=mid-1;else low=mid+1;}return high;}int main(){int xx1,xx2,i;while(scanf("%d",&n)!=EOF&&n){scanf("%d%d%d%d%d",&m,&x1,&y1,&x2,&y2);memset(a,0,sizeof(a));memset(b,0,sizeof(b));line[0]=makeline(x1,y1,x1,y2);for(i=1;i<=n;i++){scanf("%d%d",&xx1,&xx2);line[i]=makeline(xx1,y1,xx2,y2);}line[n+1]=makeline(x2,y1,x2,y2);sort(line,line+n+1,Comp1);Point p;for(i=1;i<=m;i++){scanf("%d%d",&p.x,&p.y);a[binary_search(p)]++;}for(i=0;i<=n;i++)b[a[i]]++;printf("Box\n");for(i=1;i<=m;i++)if(b[i])printf("%d: %d\n",i,b[i]);}return 0;}

POJ 3304 Segment
题意:判断是否存在一条直线,使得n条线段在该直线上的投影存在公共部分。
分析:若存在这样一条直线,过投影相交区域作直线的垂线,该垂线必定与每条线段相交,问题转化为问是否存在一条线和所有线段相交;
若存在一条直线与所有线段相机相交,将该线旋转,平移,直到不能再动为止,此时该直线必定经过这些线段的某两个端点;
所以枚举任意两个端点即可,注意还要判重。(此思路是借鉴的别人的)
代码:

#include <stdio.h>#include<cmath> const double ESP=1e-8;const int MAXN=210;struct Point{double x,y;}point[MAXN];int dblcmp(double d){  if(fabs(d) <ESP)return 0;  else return d > 0 ? 1 : -1;  }  bool isEqual(double &a,double &b){return fabs(a-b)<ESP;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool linecross(Point a,Point b,Point c,Point d)//过a,b的直线{return dblcmp(multi(a,c,b))*dblcmp(multi(a,d,b))<=0;}int main(){bool isfind;int T,n;scanf("%d",&T);while(T--){isfind=false;scanf("%d",&n);for(int i=1;i<=2*n;i+=2)scanf("%lf%lf%lf%lf",&point[i].x,&point[i].y,&point[i+1].x,&point[i+1].y);for(int i=1;i<=2*n&&!isfind;i++)for(int j=i+1;j<=2*n;j++){if(isEqual(point[i].x,point[j].x)&&isEqual(point[i].y,point[j].y))continue;//判重int k;for(k=1;k<2*n;k+=2){if(!linecross(point[i],point[j],point[k],point[k+1]))break;}if(k>=2*n){isfind=true;break;}}if(isfind)printf("Yes!\n");else printf("No!\n");}return 0;}

POJ 1269 Intersecting Lines
题意:直线相交判断(不相交、共线、相交),相交求交点
分析:没啥好分析的,略啦
代码:

#include <stdio.h>#include <cmath>const double ESP=1e-8;#define isequal(x,y)(fabs(x-y)<ESP)int dblcmp(double d){if(fabs(d)<ESP)return 0;return (d>0)?1:-1;}struct Point{double x,y;}p[5];struct Line{double a,b,c;}line[2];Line lineFromSegment(Point p1, Point p2){//线段所在直线,返回直线方程的三个系数 Line tmp;tmp.a = p2.y - p1.y;tmp.b = p1.x - p2.x;tmp.c = p2.x * p1.y - p1.x * p2.y;return tmp;}Point LineInter(Line l1, Line l2){//求两直线得交点坐标Point tmp; if(fabs(l1.b) < ESP){ tmp.x = -l1.c / l1.a;  tmp.y = (-l2.c - l2.a * tmp.x) / l2.b;}       else{tmp.x = (l1.c * l2.b - l1.b * l2.c) / (l1.b * l2.a - l2.b * l1.a);tmp.y = (-l1.c - l1.a * tmp.x) / l1.b;}return tmp;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}int main(){int T;scanf("%d",&T);for(int i=0;i<T;i++){scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&p[0].x,&p[0].y,&p[1].x,&p[1].y,&p[2].x,&p[2].y,&p[3].x,&p[3].y);if(i==0)printf("INTERSECTING LINES OUTPUT\n");line[0]=lineFromSegment(p[0],p[1]);line[1]=lineFromSegment(p[2],p[3]);if(isequal(line[0].a*line[1].b,line[0].b*line[1].a))//isequal(line[0].a/line[0].b,line[1].a/line[1].b)是wrong answer,因为浮点除法有误差{if(!dblcmp(multi(p[0],p[1],p[2]))&&!dblcmp(multi(p[0],p[1],p[3])))//四点共线printf("LINE\n");else printf("NONE\n");}else{p[4]=LineInter(line[0],line[1]);printf("POINT %.2lf %.2lf\n",p[4].x,p[4].y);}}printf("END OF OUTPUT\n");return 0;}

POJ 1556 The Doors
题意:求从(0,5)到(10,5)的最短距离,之间有墙和门。
分析:黑书上的一道习题,个人觉得此题主要是Dijkstra算法的应用,其次是线段相交的判断。
代码:

#include <stdio.h>#include <math.h>#include <vector>using namespace std;struct Point{double x,y;};struct Seg{Point a,b;}seg[60];vector<double>data[21];double min(double x,double y){return x<y?x:y;} double max(double x,double y){return x>y?x:y;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool isIntersected(double x1,double y1,double x2,double y2,double x3,double y3,double x4,double y4){Point s1,e1,s2,e2;s1.x=x1;s1.y=y1;e1.x=x2;e1.y=y2;s2.x=x3;s2.y=y3;e2.x=x4;e2.y=y4;if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >0))  return true;return false;    }double distance(double ax,double ay,double bx,double by){  return sqrt((ax-bx)*(ax-bx)+(ay-by)*(ay-by));  }int fun(int x)//计算第x个墙的第一条线段的编号{if(x==0)return 0;return (x-1)*3;}int main(){int n;double dist[21][4];while(~scanf("%d",&n)&&n!=-1){for(int i=0;i<21;i++)data[i].clear();data[0].push_back(0);data[0].push_back(5.0);int num_seg=0;for(int i=1;i<=n;i++){double hh;for(int j=0;j<5;j++){scanf("%lf",&hh);data[i].push_back(hh);}seg[num_seg].a.x=seg[num_seg].b.x=data[i][0];seg[num_seg].a.y=0;seg[num_seg].b.y=data[i][1];seg[num_seg+1].a.x=seg[num_seg+1].b.x=data[i][0];seg[num_seg+1].a.y=data[i][2];seg[num_seg+1].b.y=data[i][3];seg[num_seg+2].a.x=seg[num_seg+2].b.x=data[i][0];seg[num_seg+2].a.y=data[i][4];seg[num_seg+2].b.y=10.0;num_seg+=3;}data[n+1].push_back(10.0);data[n+1].push_back(5.0);for(int i=0;i<=n+1;i++)for(int j=0;j<4;j++)dist[i][j]=1000;dist[0][0]=0;for(int i=1;i<=n+1;i++)for(int j=1;j<data[i].size();j++){for(int p=0;p<i;p++)for(int q=1;q<data[p].size();q++){int k;for(k=fun(p);k<fun(i);k++)//亦可以 (k=0;k<num_seg;k++);当数据量很大时,前者效率高一点,但此题因数据少无所谓{if(isIntersected(data[i][0],data[i][j],data[p][0],data[p][q],seg[k].a.x,seg[k].a.y,seg[k].b.x,seg[k].b.y))break;}if(k==fun(i))dist[i][j-1]=min(dist[i][j-1],dist[p][q-1]+distance(data[i][0],data[i][j],data[p][0],data[p][q]));}}printf("%.2lf\n",dist[n+1][0]);}return 0;}

POJ 2653 Pick-up sticks
题意:按顺序放置一定数量的枝条(线段),找到所有的不被压的枝条。
分析:针对每一个枝条,遍历在其后放的枝条,判断是否相交,若都不相交,则是符合条件的。
代码:

#include <stdio.h>#include <string.h>const int MAXN=100005;struct Point{double x,y;};struct Seg{Point a,b;}seg[MAXN];double min(double x,double y){return x<y?x:y;} double max(double x,double y){return x>y?x:y;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool isIntersected(Point s1, Point e1, Point s2, Point e2){if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0))  return true;return false;    }int mark[MAXN];int main(){int n;while(scanf("%d",&n)!=EOF){if(n==0)break;memset(mark,0,sizeof(mark));for(int i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&seg[i].a.x,&seg[i].a.y,&seg[i].b.x,&seg[i].b.y);for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++){if(isIntersected(seg[i].a,seg[i].b,seg[j].a,seg[j].b)){mark[i]=1;break;}}printf("Top sticks: ");   int k;for(k=1;k<=n;k++)if(!mark[k]){printf("%d",k);break;}for(int i=k+1;i<=n;i++)if(!mark[i])printf(", %d",i);printf(".\n");}return 0;}

POJ 1066 Treasure Hunt
题意:在一个矩形区域内,有n条线段,线段的端点是在矩形边上的有一个特殊点end,问从这个点到矩形边的最少经过的线段(实际穿过线段时只能穿过中点)
分析:首先要要理解穿过最少门的意思,其实只要枚举end与矩形边上每小段的中点mid连线与几条线段相交就OK了。
代码:

#include <stdio.h>#include <vector>#include <algorithm>using namespace std;struct Point{double x,y;}p,q[4];struct Seg{Point a,b;}seg[31];vector<Point>v[4];bool Comp1(Point a,Point b){return a.x<b.x;}bool Comp2(Point a,Point b){return a.y<b.y;}bool Comp3(Point a,Point b){return a.x>b.x;}bool Comp4(Point a,Point b){return a.y>b.y;}void devide(Point po){if(po.y==0)v[0].push_back(po);else if(po.x==100.0)v[1].push_back(po);else if(po.y==100.0)v[2].push_back(po);else v[3].push_back(po);}Point getmid(Point p1,Point p2){Point temp;temp.x=(p1.x+p2.x)/2.0;temp.y=(p1.y+p2.y)/2.0;return temp;}double min(double x,double y){return x<y?x:y;} double max(double x,double y){return x>y?x:y;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool isIntersected(Point s1, Point e1, Point s2, Point e2){if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0))  return true;return false;    }int main(){int n;Point end;scanf("%d",&n);q[0].x=0;q[0].y=0;q[1].x=100.0;q[1].y=0;q[2].x=100.0;q[2].y=100.0;q[3].x=0;q[3].y=100.0;for(int i=0;i<4;i++)v[i].push_back(q[i]);for(int i=0;i<n;i++){scanf("%lf%lf",&p.x,&p.y);devide(p);seg[i].a=p;scanf("%lf%lf",&p.x,&p.y);devide(p);seg[i].b=p;}scanf("%lf%lf",&end.x,&end.y);for(int i=0;i<3;i++)v[i].push_back(q[i+1]);v[3].push_back(q[0]);sort(v[0].begin(),v[0].end(),Comp1);sort(v[1].begin(),v[1].end(),Comp2);sort(v[2].begin(),v[2].end(),Comp3);sort(v[3].begin(),v[3].end(),Comp4);Point mid;Seg s;int big,now;big=30;for(int i=0;i<4;i++){for(int j=0;j<v[i].size()-1;j++){now=0;mid=getmid(v[i][j],v[i][j+1]);s.a=mid;s.b=end;for(int k=0;k<n;k++){if(isIntersected(mid,end,seg[k].a,seg[k].b))now++;}if(now<big)big=now;}}printf("Number of doors = %d\n",big+1);return 0;}

POJ 1410 Intersection

题意:判断给定线段是否与矩形相交

分析:很简单的题,将线段与矩形的四条边一次比较即可。此题还需注意的有:(1)线段在矩形内也算相交,不然过不了;(2)给出的左上顶点和右下顶点不保证x1<x2,y1>y2;即需要自己判断

代码:

#include <stdio.h>struct Point{int x,y;};struct Seg{Point a,b;}seg[5];int xleft,xright,ybottom,ytop;Seg makeseg(int x1,int y1,int x2,int y2){Seg temp;temp.a.x=x1;temp.a.y=y1;temp.b.x=x2;temp.b.y=y2;return temp;}int min(int x,int y){return x<y?x:y;} int max(int x,int y){return x>y?x:y;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool isIntersected(Point s1, Point e1, Point s2, Point e2){if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0))  return true;return false;    }bool inrectangle(){if((max(seg[0].a.x,seg[0].b.x)<xright)&&(min(seg[0].a.x,seg[0].b.x)>xleft)&&(max(seg[0].a.y,seg[0].b.y)<ytop)&&(min(seg[0].a.y,seg[0].b.y)>ybottom))return true;return false;}int main(){int n;int flag;scanf("%d",&n);while(n--){flag=0;int x1,y1,x2,y2;scanf("%d%d%d%d",&seg[0].a.x,&seg[0].a.y,&seg[0].b.x,&seg[0].b.y);scanf("%d%d%d%d",&x1,&y1,&x2,&y2);xleft=min(x1,x2);xright=max(x1,x2);ytop=max(y1,y2);ybottom=min(y1,y2);//scanf("%d%d%d%d",&xleft,&ytop,&xright,&ybottom);seg[1]=makeseg(xleft,ybottom,xright,ybottom);seg[2]=makeseg(xright,ybottom,xright,ytop);seg[3]=makeseg(xleft,ytop,xright,ytop);seg[4]=makeseg(xleft,ybottom,xleft,ytop);if(inrectangle())flag=1;elsefor(int i=1;i<=4;i++){if(isIntersected(seg[0].a,seg[0].b,seg[i].a,seg[i].b)){flag=1;break;}}if(flag)printf("T\n");else printf("F\n");}return 0;}

POJ 1696 Space Ant

题意:一只蚂蚁,只会向左转,现在给出平面上很多个点,求解一种走法,能使得蚂蚁能经过的点最多,每个顶点该蚂蚁只能经过一次,且所行走的路线不能发生交叉。

分析:这题考查了凸包的性质,是Graham算法的应用。先选取最左下方的(下的优先级比左高)点,然后对剩下的点进行极角排序,选取最小的;重复操作,直至所有的点都被选中输出。其实无论输入什么样的点集,一定可以走完全部n个点的,这是凸包的性质决定。

代码:

#include <stdio.h>#include<math.h>#include <algorithm>using namespace std;struct Point{int x,y,code;};Point p[51];int count1;int multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}double Distance(Point a,Point b){  //若函数名为distance,则跟std::distance冲突了return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)*1.0);  }bool Comp(Point a,Point b){int temp=multi(a,b,p[count1]);if(temp>0)return true;else if(temp==0&&Distance(a,p[count1])<Distance(b,p[count1]))return true;return false;}void swap(Point &a,Point &b){Point p1;p1=a;a=b;b=p1;}int main(){int T;int n;scanf("%d",&T);while(T--){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d%d",&p[i].code,&p[i].x,&p[i].y);if(p[i].y<p[1].y)swap(p[1],p[i]);}printf("%d %d",n,p[1].code);count1=1;while(count1<n){sort(p+count1+1,p+n+1,Comp);printf(" %d",p[count1+1].code);count1++;}printf("\n");}return 0;}

POJ 3347 Kadj Squares

题意:给n个正方形,要求45°角放置,最左边的正方形紧贴Y轴,所有的正方形的下面的端点都在X轴上。然后按照正方形不能交错但要尽可能的挨着的原则摆放,找到所有从上往下看能看到的正方形。

分析:感觉这题比较复杂,是看了discuss和解题报告才AC了的。这题是转化为线段来做,直接将每个正方形的与X轴平行的角对角线作为处理对象。先确定线段的左右顶点,然后暴力扫描判断线段是否被遮挡。另外,因为该线段长度是根号二的倍数,为了避免小数的运算,可将线段长度扩大2倍,将边长乘以2即可。

代码:

#include <stdio.h>#include <cmath>struct Seg{int left,right,len;}seg[51];int max(int x,int y){if(x>y)return x;return y;}int main(){int n;while(~scanf("%d",&n)&&n){for(int i=0;i<n;i++){scanf("%d",&seg[i].len);seg[i].left=0;for(int j=0;j<i;j++)seg[i].left=max(seg[i].left,seg[j].right-abs(seg[i].len-seg[j].len));seg[i].right=seg[i].left+(seg[i].len<<1);}for(int i=0;i<n;i++){for(int j=0;j<i;j++){if(seg[i].left<seg[i].right){if(seg[i].left<seg[j].right){if(seg[i].len<seg[j].len)seg[i].left=seg[j].right;else seg[j].right=seg[i].left;}}else break;}}int k;for(k=0;k<n;k++)if(seg[k].left < seg[k].right)  { printf("%d",k+1);break;}for(int i = k+1; i < n; i++)  {  if(seg[i].left < seg[i].right)  printf(" %d",i+1);}  printf("\n");  } return 0;}

POJ 2826 An Easy Problem?!

题意:由两块木板组成的装置,求能盛放的水量。

分析:显然没想象的那么简单,需要考虑的有很多。注意点:两条线不相交,左边或右边的口被遮住,交点是某条线的那个纵坐标较高的那点某条线段水平放置。

代码:

#include <stdio.h>#include <cmath>#define ESP 1e-8 struct Point{double x,y;}p[4];struct Line{double a,b,c;}line[2],l;Point getp(Point a,Point b,double y0){if(y0==b.y)return b;double x0=fabs(a.x-(y0-a.y)*fabs((a.x-b.x))/(b.y-a.y));Point temp;temp.x=x0;temp.y=y0;return temp;}double min(double x,double y){return x<y?x:y;} double max(double x,double y){return x>y?x:y;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool isIntersected(Point s1, Point e1, Point s2, Point e2){//判断线段[s1, e1]和[s2, e2]是否相交//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交 //2.跨立试验if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0))  return true;return false;    }Line lineFromSegment(Point p1, Point p2){//线段所在直线,返回直线方程的三个系数 Line tmp;tmp.a = p2.y - p1.y;tmp.b = p1.x - p2.x;tmp.c = p2.x * p1.y - p1.x * p2.y;return tmp;}Point LineInter(Line l1, Line l2){//求两直线得交点坐标Point tmp; if(fabs(l1.b) < ESP){ tmp.x = -l1.c / l1.a;  tmp.y = (-l2.c - l2.a * tmp.x) / l2.b;}       else{tmp.x = (l1.c * l2.b - l1.b * l2.c) / (l1.b * l2.a - l2.b * l1.a);tmp.y = (-l1.c - l1.a * tmp.x) / l1.b;}return tmp;}void swap(Point &a,Point &b){Point temp;temp=a;a=b;b=temp;}int main(){int n;double s;scanf("%d",&n);int de=1;while(n--){for(int i=0;i<4;i++)scanf("%lf%lf",&p[i].x,&p[i].y);//printf("Case %d:",de++);Point cross_point;if(!isIntersected(p[0],p[1],p[2],p[3]))printf("0.00\n");else//相交{line[0]=lineFromSegment(p[0],p[1]);line[1]=lineFromSegment(p[2],p[3]);cross_point=LineInter(line[0],line[1]);int num=0;//计算比交点高的点数Point cp[2];for(int i=0;i<4;i++)if(p[i].y>cross_point.y)cp[num++]=p[i];if(num<=1)printf("0.00\n");else{if(cp[0].y>cp[1].y)swap(cp[0],cp[1]);if((cp[0].x>cross_point.x&&cp[0].x<=cp[1].x&&multi(cp[1],cp[0],cross_point)<0)||(cp[0].x>=cp[1].x&&cp[0].x<cross_point.x&&multi(cp[1],cp[0],cross_point)>0))printf("0.00\n");else{Point p1,p2;    p1.x=cp[0].x+1;p1.y=cp[0].y;line[0]=lineFromSegment(p1,cp[0]);line[1]=lineFromSegment(cross_point,cp[1]);p2=LineInter(line[0],line[1]);s=fabs(multi(p2,cp[0],cross_point))/2.0;printf("%.2lf\n",s);}}}}return 0;}



POJ 1039 Pipe

题意:有一宽度为1的折线管道,上面各顶点为(X0,Y0),(X1,Y1),……,(Xn,Yn),下面各顶点为(X0,Y0-1),(X1,Y1-1),……,(Xn,Yn-1),假设管壁都是不透明、不反射的,光线从左边入口处的(X0,Y0),(X0,Y0-1)之间射入,向四面八方直线传播,问光线最远能射到哪里(x坐标)或者能穿透整个管壁。

分析:本题上下顶点对于限制光线非常关键。解决方法:任取两个顶点,判断这两点所在直线是否能从入口射进到达该处,若能,则继续向后判断,看能否穿过其后的所有线段(Xi,Yi)(Xi,Yi-1),一旦与线段(Xk,Yk)(Xk,Yk-1)不相交,则交点必在前方,求交点。大概思路就这样,还算比较简洁。

代码:

#include <stdio.h>#include<cmath> #define ESP 1e-8 #define INF 99999int dblcmp(double d){if(fabs(d)<ESP)return 0;return (d>0)?1:-1;}struct Point{double x,y;}p[42];struct Line{double a,b,c;};Line lineFromSegment(Point p1, Point p2){//线段所在直线,返回直线方程的三个系数 Line tmp;tmp.a = p2.y - p1.y;tmp.b = p1.x - p2.x;tmp.c = p2.x * p1.y - p1.x * p2.y;return tmp;}double cross(Point a,Point b,Point c){return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);}//判断直线ab和线段cd是否相交,相交为true,不相交为false bool linecross(Point a,Point b,Point c,Point d){int d1,d2;d1=dblcmp(cross(a,c,b));d2=dblcmp(cross(a,d,b));if(d1*d2>0) return false;return true;}bool linecross1(Point a,Point b,Point c,Point d){int d1,d2;d1=dblcmp(cross(a,c,b));d2=dblcmp(cross(a,d,b));if(d1*d2>=0) return false;return true;}Point LineInter(Point a,Point b,Point c,Point d){//求两直线得交点坐标Line l1,l2;l1=lineFromSegment(a,b);l2=lineFromSegment(c,d);Point tmp;if(fabs(l1.b) < ESP){tmp.x = -l1.c / l1.a;  tmp.y = (-l2.c - l2.a * tmp.x) / l2.b;}       else{tmp.x = (l1.c * l2.b - l1.b * l2.c) / (l1.b * l2.a - l2.b * l1.a);tmp.y = (-l1.c - l1.a * tmp.x) / l1.b;}return tmp;}int main(){int n;int flag;while(~scanf("%d",&n)&&n){flag=0;for(int i=0,j=n;i<n;i++,j++){scanf("%lf%lf",&p[i].x,&p[i].y);p[j].x=p[i].x;p[j].y=p[i].y-1;}double max=-1*INF ,now;for(int i=0;i<n;i++){for(int j=n;j<2*n;j++){if(i+n==j)continue;int k,rp=0,h;if(i>j-n)h=i;else h=j-n;for(k=0;k<h;k++)if(!linecross(p[i],p[j],p[k],p[k+n])){rp=1;break;}if(rp)continue;//该直线不可能从入口射进或射进但被挡住达不到i,j处for(k=h+1;k<n;k++){if(!linecross(p[i],p[j],p[k],p[k+n])){if(linecross1(p[i],p[j],p[k-1],p[k])){now=LineInter(p[i],p[j],p[k-1],p[k]).x;}else now=LineInter(p[i],p[j],p[k+n-1],p[k+n]).x;if(now>max)max=now;break;}}if(k==n){flag=1;break;}}if(flag)break;}if(flag)printf("Through all the pipe.\n");else printf("%.2lf\n",max);}return 0;}

POJ 3449 Geometric Shapes

题意:给一些几何图形,判断相交情况,按要求输出。

分析:题目不难,但很复杂,很烦。暴力枚举,只要注意输入输出就行。还有一个知识点,就是如何根据所给正方形(边不与轴平行)的两个对角线上的顶点,求另外两个点。

方法如下:

已知正方形的一对不相邻的顶点(x0,y0),(x2,y2),可以由方程组:

x1 + x3 = x0 + x2;

x1 - x3 = y2 - y0;

y1 + y3 = y0 + y2;

y3 - y1 = x2 - x0;

求得另一对不相邻的顶点(x1,y1),(x3,y3)。

代码:

#include <stdio.h>#include <algorithm>#include <string.h>#include<vector>using namespace std;struct Point{double x,y;};struct Shape{char label;int pn;Point p[20];}shape[31];vector<char>v[30];Point input(){Point tmp;char c;while((c=getchar())!=')'){if(c=='(')scanf("%lf",&tmp.x);else if(c==',')scanf("%lf",&tmp.y);}return tmp;}bool comp(Shape s1,Shape s2){return s1.label<s2.label;}double min(double x,double y){return x<y?x:y;} double max(double x,double y){return x>y?x:y;}double multi(Point p1, Point p2, Point p0){return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool isintersected(Point s1, Point e1, Point s2,Point e2){if((max(s1.x, e1.x) >= min(s2.x, e2.x)) &&(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0))  return true;return false;    }bool isintersect(Shape s1,Shape s2){for(int i=0;i<s1.pn-1;i++){int j;for(j=0;j<s2.pn-1;j++)if(isintersected(s1.p[i],s1.p[i+1],s2.p[j],s2.p[j+1]))return true;if(isintersected(s1.p[i],s1.p[i+1],s2.p[j],s2.p[0]))return true;}for(int i=0;i<s2.pn-1;i++)if(isintersected(s1.p[s1.pn-1],s1.p[0],s2.p[i],s2.p[i+1]))return true;if(isintersected(s1.p[s1.pn-1],s1.p[0],s2.p[s2.pn-1],s2.p[0]))return true;return false;}int main(){char c;char name[20];int count=0;while(~scanf("%c",&c)&&c!='.'){if(c=='-'){sort(shape,shape+count,comp);for(int i=0;i<30;i++)v[i].clear();for(int i=0;i<count;i++){for(int j=i+1;j<count;j++){if(isintersect(shape[i],shape[j])){v[i].push_back(shape[j].label);v[j].push_back(shape[i].label);}}}for(int i=0;i<count;i++){printf("%c ",shape[i].label);if(v[i].size()==0)printf("has no intersections\n");else{printf("intersects with ");if(v[i].size()==1)printf("%c\n",v[i][0]);else if(v[i].size()==2)printf("%c and %c\n",v[i][0],v[i][1]);else{for(int j=0;j<v[i].size()-1;j++)printf("%c, ",v[i][j]);printf("and %c\n",v[i][v[i].size()-1]);}}}printf("\n");count=0;}else if(c>='A'&&c<='Z'){shape[count].label=c;scanf("%s",name);if(!strcmp(name,"square")){shape[count].pn=4;Point temp0,temp2;temp0=input();temp2=input();shape[count].p[0]=temp0;shape[count].p[2]=temp2;shape[count].p[1].x=(temp0.x+temp2.x+temp2.y-temp0.y)/2.0;shape[count].p[1].y=(temp0.y+temp2.y-temp2.x+temp0.x)/2.0;shape[count].p[3].x=temp0.x+temp2.x-shape[count].p[1].x;shape[count].p[3].y=temp2.x-temp0.x+shape[count].p[1].y;}else if(!strcmp(name,"line")){shape[count].pn=2;shape[count].p[0]=input();shape[count].p[1]=input();}else if(!strcmp(name,"rectangle")){shape[count].pn=4;shape[count].p[0]=input();shape[count].p[1]=input();shape[count].p[2]=input();shape[count].p[3].x=shape[count].p[0].x+shape[count].p[2].x-shape[count].p[1].x;shape[count].p[3].y=shape[count].p[0].y+shape[count].p[2].y-shape[count].p[1].y;}else if(!strcmp(name,"triangle")){shape[count].pn=3;shape[count].p[0]=input();shape[count].p[1]=input();shape[count].p[2]=input();}else// if(name=="polygon"){scanf("%d",&shape[count].pn);for(int i=0;i<shape[count].pn;i++)shape[count].p[i]=input();}count++;}}return 0;}

POJ 1584 A Round Peg in a Ground Hole

题意:判断一个多边形是否为凸多边形,如果不是则输出”HOLE IS ILL-FORMED“,如果是则继续判断给定的一个圆是否在该凸多边形内,如果不在输出”PEG WILL NOT FIT“,否则输出”PEG WILL FIT“;

分析:判断是否为凸多边形,用向量叉积即可。判断圆是否在多边形内,先通过环顾法判断圆心是否在多边形内(环顾法黑书上有介绍),然后再判断是否圆心到各边的距离都大于半径。

代码:

#include <stdio.h>#include <cmath>const double ESP=1e-8;const double pi=3.141592654;  struct Point{double x,y;}p[100];struct Line{double a,b,c;};int dblcmp(double d){if(fabs(d)<ESP)return 0;return (d>0)?1:-1;}Line lineFromSegment(Point p1, Point p2){//线段所在直线,返回直线方程的三个系数 Line tmp;tmp.a = p2.y - p1.y;tmp.b = p1.x - p2.x;tmp.c = p2.x * p1.y - p1.x * p2.y;return tmp;}int n;double cx,cy,cr;double multi(Point a,Point b,Point c)//连续三点{return (b.x-a.x)*(c.y-b.y)-(c.x-b.x)*(b.y-a.y);}double pointmulti(Point a,Point b,Point c)//点积ab*ac{return (b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y);}bool isill_formed()//判断是否为凹的{double dirction=multi(p[0],p[1],p[2]);for(int i=1;i<=n-3;i++)if(multi(p[i],p[i+1],p[i+2])*dirction<0)return true;if(multi(p[n-2],p[n-1],p[0])*dirction<0||multi(p[n-1],p[0],p[1])*dirction<0)return true;return false;}double dis(double cx,double cy,Point a,Point b)//圆心(cx,cy)到线段a,b的距离{Line l=lineFromSegment(a,b);double dist;dist=abs(l.a*cx+l.b*cy+l.c)/sqrt(l.a*l.a+l.b*l.b);return dist;}double dis1(double cx,double cy,Point p)//圆心到顶点的距离{return sqrt((p.x-cx)*(p.x-cx)+(p.y-cy)*(p.y-cy));}bool iscir_in_pol(){double total=0;Point cp;cp.x=cx;cp.y=cy;for(int i=0;i<n-1;i++)//环顾法判断圆心是否在多边形内total+=acos(pointmulti(cp,p[i],p[i+1])/(dis1(cx,cy,p[i])*dis1(cx,cy,p[i+1])));total+=acos(pointmulti(cp,p[n-1],p[0])/(dis1(cx,cy,p[n-1])*dis1(cx,cy,p[0])));if(dblcmp(total-2*pi)!=0)return false;for(int i=0;i<n-1;i++)//圆心到各边的距离满足条件{if(dis(cx,cy,p[i],p[i+1])<cr)return false;}if(dis(cx,cy,p[0],p[n-1])<cr)return false;return true;}int main(){while(~scanf("%d",&n)&&n>2){scanf("%lf%lf%lf",&cr,&cx,&cy);for(int i=0;i<n;i++){scanf("%lf%lf",&p[i].x,&p[i].y);}if(isill_formed())printf("HOLE IS ILL-FORMED\n");else{if(!iscir_in_pol())printf("PEG WILL NOT FIT\n");else printf("PEG WILL FIT\n");}}return 0;}

POJ 2074 Line of Sight 

题意:给出House和Property Line的位置,还有若干个障碍物的位置,求出在Property Line上的最长的线段,满足在该线段上的所有的点都能看到完整的房子;若不存在这样的线段则输出“No View”。

分析:首先有个注意点说一下,障碍物可能不在house与Property Line之间,得对输入数据进行过滤。然后,针对每个障碍物,求其在Property Line上可遮挡住的地方。实现方法就是求house.right与line.left(障碍物的左顶点)的所在直线与pl的交点;求house.left与line.right的所在直线与pl的交点。这样就可求得遮挡的地方(其实就是线段);再然后,求所有遮挡住的地方的交集;最后扫描一遍即可求得最大长度。

代码:

#include<stdio.h>#include <vector>#include<cmath> #include <algorithm>#define ESP 1e-8 using namespace std;int dblcmp(double d){if(fabs(d)<ESP)return 0;return (d>0)?1:-1;}struct Point{double x,y;};struct Object{Point left,right;}house,pl,line;struct Line{double a,b,c;};struct Seg{double xl,xr;}seg;Line lineFromSegment(Point p1, Point p2){//线段所在直线,返回直线方程的三个系数 Line tmp;tmp.a = p2.y - p1.y;tmp.b = p1.x - p2.x;tmp.c = p2.x * p1.y - p1.x * p2.y;return tmp;}double LineInter(Point a,Point b,Point c,Point d){//求两直线得交点坐标Line l1=lineFromSegment(a,b);Line l2=lineFromSegment(c,d);Point tmp; if(fabs(l1.b) < ESP){ tmp.x = -l1.c / l1.a;  //tmp.y = (-l2.c - l2.a * tmp.x) / l2.b;}       else{tmp.x = (l1.c * l2.b - l1.b * l2.c) / (l1.b * l2.a - l2.b * l1.a);//tmp.y = (-l1.c - l1.a * tmp.x) / l1.b;}return tmp.x;}//计算a,b,c三点的叉积,判断a,b,c三点的位置关系 double cross(Point a,Point b,Point c){return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);}//判断直线ab和线段cd是否相交,相交为true,不相交为false bool linecross(Point a,Point b,Point c,Point d){int d1,d2;d1=dblcmp(cross(a,c,b));d2=dblcmp(cross(a,d,b));if(d1*d2>0) return false;return true;}vector<Seg>v;bool comp(Seg s1,Seg s2){return s1.xl<s2.xl;}bool isbetween(double yy){if((yy>house.left.y&&yy<pl.left.y)||(yy<house.left.y&&yy>pl.left.y))return true;return false;}int main(){double x1,x2,y;int n;while(~scanf("%lf%lf%lf",&x1,&x2,&y)){if(x1==0&&x2==0&&y==0)break;house.left.x=x1;house.right.x=x2;house.right.y=house.left.y=y;scanf("%lf%lf%lf",&pl.left.x,&pl.right.x,&pl.left.y);pl.right.y=pl.left.y;scanf("%d",&n);for(int i=0;i<n;i++){scanf("%lf%lf%lf",&line.left.x,&line.right.x,&line.left.y);line.right.y=line.left.y;if(!isbetween(line.right.y))//预处理{i--;n--;}else{seg.xl=LineInter(house.right,line.left,pl.left,pl.right);if(seg.xl>=pl.right.x){i--;n--;continue;}else if(seg.xl<pl.left.x)seg.xl=pl.left.x;seg.xr=LineInter(house.left,line.right,pl.left,pl.right);if(seg.xr<=pl.left.x){i--;n--;continue;}else if(seg.xr>pl.right.x)seg.xr=pl.right.x;v.push_back(seg);}}//求集合double max=0.0;if(v.size()>0){double length=0,sx,ex;sx=ex=pl.left.x;sort(v.begin(),v.end(),comp);for(int i=0;i<v.size();i++){if(v[i].xl>ex){length=v[i].xl-ex;if(length>max)max=length;sx=v[i].xl;ex=v[i].xr;}else if(v[i].xr>ex)ex=v[i].xr;}length=pl.right.x-ex;if(length>max)max=length;}else max=pl.right.x-pl.left.x;if(max>0)printf("%.2lf\n",max);else printf("No View\n");v.clear();}return 0;}

完毕。。。。

原创粉丝点击