计算几何(一):叉积的简单应用

 这是学习《ACM-ICPC程序设计系列计算几何》自己AC的第一个计算几何的问题。题目是比较简单的,但还是花了我很久的时间。

    题目可以抽象成:一个长方形被n条不相交的线段分隔成n+1个区间,给定m个点的坐标,计算出每个区间里各有多少个点。

    需要注意的是:输入时,分隔长方形的线段已经排序。(这意味着可以用二分查找)

       我的思路很简单:把区间构造起来,(这里我把长方形比喻为盒子,中间加入的线段我称作隔板。)然后,每个点去二分搜索,找到自己处在的区间就OK了。最后输出结果。

    这里面的难点就在于:我们怎么去判断一个点在一条线段的左侧还是右侧呢?这就要用到向量叉积。叉积的一个非常重要的性质是通过它的符号判断两向量相互之间的顺逆时针关系:设向量P=(x1,y1),Q=(x2,y2)

如果P*Q>0PQ的顺时针方向;

如果P*Q=0PQ共线,可能同向,与可能反向;

如果P*Q<0PQ的逆时针方向。

  (请注意这里是叉乘)

    这道题在几何上就考了这一个知识点,如果我们一步一步来,理清关系,其实是很简单的!

代码:

 
 
  1. /* 
  2.  * poj_2318.cpp 
  3.  * 
  4.  *  Created on: 规定上面是起点,下面是终点;左面是起点,右面是终点 
  5.  *      Author: Administrator 
  6.  */ 
  7. #include<stdio.h> 
  8. #include<string.h> 
  9. #define M 5005 
  10. struct Point{ 
  11.     double x,y; 
  12. }; 
  13. struct Segment{ 
  14.     Point start,end; 
  15. }; 
  16.  
  17. int bin[M]; 
  18. Segment box[M];//表示盒子 
  19.  
  20. int n,m; 
  21. double x1,y1,x2,y2;//表示盒子最外面的边框 
  22.  
  23. /* 
  24.  *点p置于线段s的哪一侧:0 左侧,1 右侧 
  25.  * */ 
  26. int isLeft(const Point &p,const Segment &s){ 
  27.     double ans; 
  28.     ans=(p.x-s.end.x)*(s.start.y-s.end.y)-(s.start.x-s.end.x)*(p.y-s.end.y); 
  29.     if(ans<0)return 1; 
  30.     else if(ans>0)return 0; 
  31.     else{printf("ERROR!\n"); 
  32.         return -1;} 
  33. //检查这个点是否在盒子里面 
  34. bool inside(Point p){ 
  35.     if(p.x>x2||p.x<x1||p.y<y2||p.y>y1) 
  36.         return false
  37.     return true
  38. //盒子中加入隔板 
  39. void add(const double &x1,const double &y1,const double &x2,const double &y2, int i){ 
  40.     box[i].start.x=x1;box[i].start.y=y1; 
  41.     box[i].end.x=x2;box[i].end.y=y2; 
  42. //查找玩具对应的格子:因为已经排序,所以可以用二分查找 
  43. int solve(int left,int right,const Point &p){ 
  44.     int mid=(left+right)>>1; 
  45.     if(left==right){ 
  46.         return isLeft(p,box[mid])==1? mid: -1; 
  47.     } 
  48.     if(isLeft(p,box[mid])==1){//在mid的左边 
  49.         return solve(left,mid,p); 
  50.     }else
  51.         return solve(mid+1,right,p); 
  52.     } 
  53. int main(){ 
  54.     int id; 
  55.     Point toy; 
  56.     while(1){ 
  57.         scanf("%d",&n); 
  58.         if(n==0)break
  59.         scanf("%d",&m); 
  60.         scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2); 
  61.         add(x1,y1,x1,y2,0); 
  62.         add(x2,y1,x2,y2,n+1); 
  63.         double ui,li; 
  64.         for(int i=1;i<=n;i++){ 
  65.             scanf("%lf %lf",&ui,&li); 
  66.             add(ui,y1,li,y2,i);//盒子里添加隔板 
  67.         } 
  68.  
  69.         memset(bin,0,(n+1)*sizeof(int)); 
  70.         for(int i=0;i<m;i++){ 
  71.             scanf("%lf %lf",&toy.x,&toy.y); 
  72.             if(inside(toy)){//如果玩具确实扔到盒子里了,就计算玩具所在的格子 
  73.                 id=solve(1,n+1,toy); 
  74.                 if(id>0)bin[id-1]++; 
  75.                 else printf("ERROR!\n"); 
  76.             } 
  77.         } 
  78.         //print result 
  79.         for(int i=0;i<=n;i++){ 
  80.             printf("%d: %d\n",i,bin[i]); 
  81.         } 
  82.         printf("\n"); 
  83.     } 
  84.     return 0; 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值