POJ 计算几何专项训练(1) 【2318】&【2398】&【3304】&【2653】&【1556】&【1066】

POJ 2318 TOYS

题意是在一个大矩形里有n条分割线把矩形分割成n+1部分、再给出一些玩具的坐标、要求统计每个部分内有多少个玩具、

具体做法就是二分求解出当前玩具右方的第一条线、这可以用叉积判断、

Code:

var
  res:array [0..5002] of longint;
  p:array [0..5002] of record x1,x2,y1,y2:double;end;
  xx,yy,ldx,ldy,rux,ruy:double;
  n,m,i,l,r,mid:longint;
function crossp(x1,y1,x2,y2:double):double;
  begin
    crossp:=x1*y2-x2*y1;
  end;
function ok(now:longint):boolean;
  begin
    if crossp(p[now].x1-xx,p[now].y1-yy,p[now].x2-xx,p[now].y2-yy)>0 then exit(true) else exit(false);
  end;
begin
  read(n);
  while n<>0 do
    begin
      readln(m,ldx,ruy,rux,ldy);
      for i:=1 to n do
        begin
          readln(p[i].x2,p[i].x1);
          p[i].y1:=ldy;
          p[i].y2:=ruy;
        end;
      p[0].x1:=ldx;p[0].x2:=ldx;
      p[0].y1:=ldy;p[0].y2:=rux;
      p[n+1].x1:=rux;p[n+1].x2:=rux;
      p[n+1].y1:=ldy;p[n+1].y2:=ruy;
      for i:=1 to m do
        begin
          readln(xx,yy);
          l:=1;r:=n+1;
          while l<r-1 do
            begin
              mid:=(l+r) div 2;
              if ok(mid) then r:=mid else l:=mid;
            end;
          while (l>1) and ok(l-1) do dec(l);
          while not ok(l) do inc(l);
          inc(res[l-1]);
        end;
      for i:=0 to n do
        writeln(i,': ',res[i]);
      writeln;
      fillchar(res,sizeof(res),0);
      read(n);
    end;
end.

  

POJ 2398 TOY Storage

本题是上题的姊妹题,区别在于本题要求统计的是包含X个玩具的部分有多少、

P.S 上题的X坐标是有序给出的,而本题要先排序、

Code:

var
  res,app:array [0..1002] of longint;
  p:array [0..1002] of record x1,x2,y1,y2:double;end;
  xx,yy,ldx,ldy,rux,ruy:double;
  n,m,i,l,r,mid:longint;
function crossp(x1,y1,x2,y2:double):double;
  begin
    crossp:=x1*y2-x2*y1;
  end;
function ok(now:longint):boolean;
  begin
    if crossp(p[now].x1-xx,p[now].y1-yy,p[now].x2-xx,p[now].y2-yy)>0 then exit(true) else exit(false);
  end;
procedure sort(l,r:longint);
  var
    i,j:longint;
    cx:double;
  begin
    i:=l;j:=r;cx:=p[random(r-l+1)+l].x1;
    repeat
      while p[i].x1<cx do inc(i);
      while cx<p[j].x1 do dec(j);
      if i<=j then
        begin
          p[1002]:=p[i];p[i]:=p[j];p[j]:=p[1002];
          inc(i);dec(j);
        end;
    until i>j;
    if i<r then sort(i,r);
    if j>l then sort(l,j);
  end;
begin
  read(n);
  while n<>0 do
    begin
      readln(m,ldx,ruy,rux,ldy);
      for i:=1 to n do
        begin
          readln(p[i].x2,p[i].x1);
          p[i].y1:=ldy;
          p[i].y2:=ruy;
        end;
      sort(1,n);
      p[n+1].x1:=rux;p[n+1].x2:=rux;
      p[n+1].y1:=ldy;p[n+1].y2:=ruy;
      for i:=1 to m do
        begin
          readln(xx,yy);
          l:=1;r:=n+1;
          while l<r-1 do
            begin
              mid:=(l+r) div 2;
              if ok(mid) then r:=mid else l:=mid;
            end;
          while (l>1) and ok(l-1) do dec(l);
          while not ok(l) do inc(l);
          inc(res[l-1]);
        end;
      for i:=0 to n do
        inc(app[res[i]]);
      writeln('Box');
      for i:=1 to n do
        if app[i]<>0 then
          writeln(i,': ',app[i]);
      fillchar(app,sizeof(app),0);
      fillchar(res,sizeof(res),0);
      read(n);
    end;
end.

  

POJ 3304 Segments

本题要求判断是否存在一条直线通过给出的所有直线、

不难想到如果该直线不过任何一条直线的端点,一定可以让它旋转一定角度与某一个端点相碰;

而如果该直线只与某一个相碰,必然可以旋转另一端点与另一个端点相碰、

于是,我们N^2枚举出直线,然后判断是否与每一条线段相交即可、

Code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>

#define EPS 1e-8
#define sqr(x) ((x)*(x))

using namespace std;

double x[501],y[501];
int n,vv;

double dis(double aa,double bb,double cc,double dd){
	return	sqrt((aa-bb)*(aa-bb)+(cc-dd)*(cc-dd));
}

double crossp(double aa,double bb,double cc,double dd){
	return	aa*dd-bb*cc;
}

int judge(double x1,double y1,double x2,double y2){
	if	(dis(x1,x2,y1,y2)<EPS)	return	0;
	for	(int j=1;j<=n*2;j++)
		if	(j&1)
			if	(crossp(x[j]-x1,y[j]-y1,x2-x1,y2-y1)*crossp(x2-x1,y2-y1,x[j+1]-x1,y[j+1]-y1)<0)	return	0;
	return 1;
}

int main(){
	scanf("%d",&vv);
	while	(vv--){
		scanf("%d",&n);
		for	(int i=1;i<=n*2;i++)
			scanf("%lf%lf",&x[i],&y[i]);
		if	(n==1){
			printf("Yes!\n");
			continue;
		}
		int succ=0;
		for	(int i=1;i<=n*2 && !succ;i++)
			for	(int j=i+1;j<=n*2 && !succ;j++)
				if	(judge(x[i],y[i],x[j],y[j]))	succ++;
		if	(succ)	printf("Yes!\n");	else	printf("No!\n");
	}
}

  

POJ 1269 Pick-up Sticks

本题来自于一个经典的……游戏?

题意主要就是每次放一条线段上去、如果后放的与先放的有交点就覆盖掉先放的、

于是我们可以维护一个随便什么链表、然后维护当前在顶上的线段编号、因为题目保证TOP线段不超过1000条、所以是可以无压力通过的、、

Code:

#include <cstring>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>

#define EPS 1e-9
#define sqr(x) ((x)*(x))
#define INF 1e9
#define pb push_back

using namespace std;

struct	segment{
	double sx,sy,ex,ey;	
}ss[100010];

int n,top[1010],tt;

double crossp(double x1,double y1,double x2,double y2){
	return x1*y2-x2*y1;
}

int cross(segment a,segment b){
	if	(max(a.sx,a.ex)<min(b.sx,b.ex) || max(a.sy,a.ey)<min(b.sy,b.ey) \
	||   min(a.sx,a.ex)>max(b.sx,b.ex) || min(a.sy,a.ey)>max(b.sy,b.ey))	return	0;
	if	(crossp(a.sx-b.sx,a.sy-b.sy,b.ex-b.sx,b.ey-b.sy)*crossp(b.ex-b.sx,b.ey-b.sy,a.ex-b.sx,a.ey-b.sy)>0	&&	\
		 crossp(b.sx-a.sx,b.sy-a.sy,a.ex-a.sx,a.ey-a.sy)*crossp(a.ex-a.sx,a.ey-a.sy,b.ex-a.sx,b.ey-a.sy)>0)	return	1;
	else	return	0;
}

int main(){
	scanf("%d",&n);
	while	(n){
		for	(int i=1;i<=n;i++)
			scanf("%lf%lf%lf%lf",&ss[i].sx,&ss[i].sy,&ss[i].ex,&ss[i].ey);
		top[tt=1]=1;
		for	(int i=2;i<=n;i++){
			for	(int j=1;j<=tt;j++)
				if	(cross(ss[top[j]],ss[i]))	top[j]=-1;
			int cur=0;
			for	(int j=1;j<=tt;j++)
				if	(top[j]>0)	top[++cur]=top[j];
			tt=cur;
			top[++tt]=i;
		}
		printf("Top sticks:");
		for	(int i=1;i<tt;i++)
			printf(" %d,",top[i]);
		printf(" %d.\n",top[tt]);
		scanf("%d",&n);
	}
	return 	0;
}

  

POJ 1556 The doors

一看图就知道是什么题了、

把所有出现的点全部建成一张图,然后求解最短路就可以了。

Code:

#include <cstring>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>

#define EPS 1e-9
#define sqr(x) ((x)*(x))
#define INF 1e9
#define pb push_back

using namespace std;

struct	segment{
	double sx,sy,ex,ey;	
}ss[100010];

int n,top[1010],tt;

double crossp(double x1,double y1,double x2,double y2){
	return x1*y2-x2*y1;
}

int cross(segment a,segment b){
	if	(max(a.sx,a.ex)<min(b.sx,b.ex) || max(a.sy,a.ey)<min(b.sy,b.ey) \
	||   min(a.sx,a.ex)>max(b.sx,b.ex) || min(a.sy,a.ey)>max(b.sy,b.ey))	return	0;
	if	(crossp(a.sx-b.sx,a.sy-b.sy,b.ex-b.sx,b.ey-b.sy)*crossp(b.ex-b.sx,b.ey-b.sy,a.ex-b.sx,a.ey-b.sy)>0	&&	\
		 crossp(b.sx-a.sx,b.sy-a.sy,a.ex-a.sx,a.ey-a.sy)*crossp(a.ex-a.sx,a.ey-a.sy,b.ex-a.sx,b.ey-a.sy)>0)	return	1;
	else	return	0;
}

int main(){
	scanf("%d",&n);
	while	(n){
		for	(int i=1;i<=n;i++)
			scanf("%lf%lf%lf%lf",&ss[i].sx,&ss[i].sy,&ss[i].ex,&ss[i].ey);
		top[tt=1]=1;
		for	(int i=2;i<=n;i++){
			for	(int j=1;j<=tt;j++)
				if	(cross(ss[top[j]],ss[i]))	top[j]=-1;
			int cur=0;
			for	(int j=1;j<=tt;j++)
				if	(top[j]>0)	top[++cur]=top[j];
			tt=cur;
			top[++tt]=i;
		}
		printf("Top sticks:");
		for	(int i=1;i<tt;i++)
			printf(" %d,",top[i]);
		printf(" %d.\n",top[tt]);
		scanf("%d",&n);
	}
	return 	0;
}

  

POJ 1066 Treasure Hunt

本题大概是说、在(0,0)(100,100)的矩形内有若干线段把矩形分割成若干区域,然后给出一个宝藏的所在地(X,Y)求一条从边界进入到达该点的路径,使得该路径通过的线段最少、

本题与3304有一定相似之处,不难发现只要枚举所有线段的端点到(X,Y)组成的线段与其它线段的交点数就可以了、

Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>

using namespace std;

struct segment{
	double beginx,beginy,endx,endy;
}ss[1010];

int n;
double nx,ny;
segment temp;

double crossp(double x1,double y1,double x2,double y2){
	return x1*y2-x2*y1;
}

int cross(segment a,segment b){
	if	(min(a.beginx,a.endx)>max(b.beginx,b.endx) ||\
	     min(b.beginx,b.endx)>max(a.beginx,a.endx) ||\
		 min(a.beginy,a.endy)>max(b.beginy,b.endy) ||\
		 min(b.beginy,b.endy)>max(a.beginy,a.endy))	return	0;
	if	(crossp(a.beginx-b.beginx,a.beginy-b.beginy,b.endx-b.beginx,b.endy-b.beginy)\
		*crossp(b.endx-b.beginx,b.endy-b.beginy,a.endx-b.beginx,a.endy-b.beginy)>0 &&\
		 crossp(b.beginx-a.beginx,b.beginy-a.beginy,a.endx-a.beginx,a.endy-a.beginy)\
		*crossp(a.endx-a.beginx,a.endy-a.beginy,b.endx-a.beginx,b.endy-a.beginy)>0)	return	1;
	return	0;
}

int main(){
	scanf("%d",&n);
	for	(int i=1;i<=n;i++)
		scanf("%lf%lf%lf%lf",&ss[i].beginx,&ss[i].beginy,&ss[i].endx,&ss[i].endy);
	scanf("%lf%lf",&nx,&ny);
	temp.endx=nx;temp.endy=ny;
	int ans=99999999;
	for	(int i=1;i<=n;i++){
		int cnt=0;
		temp.beginx=ss[i].beginx;
		temp.beginy=ss[i].beginy;
		for	(int j=1;j<=n;j++)
			if	(cross(ss[j],temp))	cnt++;
		if	(cnt<ans)	ans=cnt;
		cnt=0;
		temp.beginx=ss[i].endx;
		temp.beginy=ss[i].endy;
		for	(int j=1;j<=n;j++)
			if	(cross(ss[j],temp))	cnt++;
		if	(cnt<ans)	ans=cnt;
	}
	if	(ans==99999999)	ans=0;
	printf("Number of doors = %d\n",ans+1);
	return 0;
}

  

你可能感兴趣的:(poj)