这是学习《ACM-ICPC程序设计系列—计算几何》自己AC的第一个计算几何的问题。题目是比较简单的,但还是花了我很久的时间。
题目可以抽象成:一个长方形被n条不相交的线段分隔成n+1个区间,给定m个点的坐标,计算出每个区间里各有多少个点。
需要注意的是:输入时,分隔长方形的线段已经排序。(这意味着可以用二分查找)
我的思路很简单:把区间构造起来,(这里我把长方形比喻为盒子,中间加入的线段我称作隔板。)然后,每个点去二分搜索,找到自己处在的区间就OK了。最后输出结果。
这里面的难点就在于:我们怎么去判断一个点在一条线段的左侧还是右侧呢?这就要用到向量叉积。叉积的一个非常重要的性质是通过它的符号判断两向量相互之间的顺逆时针关系:设向量P=(x1,y1),Q=(x2,y2)
如果P*Q>0则P在Q的顺时针方向;
如果P*Q=0则P与Q共线,可能同向,与可能反向;
如果P*Q<0则P在Q的逆时针方向。
(请注意这里是叉乘)
这道题在几何上就考了这一个知识点,如果我们一步一步来,理清关系,其实是很简单的!
代码:
- /*
- * poj_2318.cpp
- *
- * Created on: 规定上面是起点,下面是终点;左面是起点,右面是终点
- * Author: Administrator
- */
- #include<stdio.h>
- #include<string.h>
- #define M 5005
- struct Point{
- double x,y;
- };
- struct Segment{
- Point start,end;
- };
- int bin[M];
- Segment box[M];//表示盒子
- int n,m;
- double x1,y1,x2,y2;//表示盒子最外面的边框
- /*
- *点p置于线段s的哪一侧:0 左侧,1 右侧
- * */
- int isLeft(const Point &p,const Segment &s){
- double ans;
- ans=(p.x-s.end.x)*(s.start.y-s.end.y)-(s.start.x-s.end.x)*(p.y-s.end.y);
- if(ans<0)return 1;
- else if(ans>0)return 0;
- else{printf("ERROR!\n");
- return -1;}
- }
- //检查这个点是否在盒子里面
- bool inside(Point p){
- if(p.x>x2||p.x<x1||p.y<y2||p.y>y1)
- return false;
- return true;
- }
- //盒子中加入隔板
- void add(const double &x1,const double &y1,const double &x2,const double &y2, int i){
- box[i].start.x=x1;box[i].start.y=y1;
- box[i].end.x=x2;box[i].end.y=y2;
- }
- //查找玩具对应的格子:因为已经排序,所以可以用二分查找
- int solve(int left,int right,const Point &p){
- int mid=(left+right)>>1;
- if(left==right){
- return isLeft(p,box[mid])==1? mid: -1;
- }
- if(isLeft(p,box[mid])==1){//在mid的左边
- return solve(left,mid,p);
- }else{
- return solve(mid+1,right,p);
- }
- }
- int main(){
- int id;
- Point toy;
- while(1){
- scanf("%d",&n);
- if(n==0)break;
- scanf("%d",&m);
- scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
- add(x1,y1,x1,y2,0);
- add(x2,y1,x2,y2,n+1);
- double ui,li;
- for(int i=1;i<=n;i++){
- scanf("%lf %lf",&ui,&li);
- add(ui,y1,li,y2,i);//盒子里添加隔板
- }
- memset(bin,0,(n+1)*sizeof(int));
- for(int i=0;i<m;i++){
- scanf("%lf %lf",&toy.x,&toy.y);
- if(inside(toy)){//如果玩具确实扔到盒子里了,就计算玩具所在的格子
- id=solve(1,n+1,toy);
- if(id>0)bin[id-1]++;
- else printf("ERROR!\n");
- }
- }
- //print result
- for(int i=0;i<=n;i++){
- printf("%d: %d\n",i,bin[i]);
- }
- printf("\n");
- }
- return 0;
- }