您的位置:首页 > 其它

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

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

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

POJ 2318 TOYS

题意:判断箱子每个块内有多少个玩具

分析:对于每个玩具的坐标,采用二分查找以确定玩具所在的块,其中玩具与分界(线段)的位置关系的判断可通过叉积来实现。

代码:

#include <stdio.h>
#include <string.h>
#define MAX 5010
struct 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 5010
struct 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;
else
for(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 99999
int 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;
}


完毕。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐