【计算几何】基础总结+题目推荐

刷了很久计算几何,好歹算是有些收获,总结一下吧。

 

计算几何不同与解析几何,这里大部分使用的是向量和点,而不是解析式。

  直线/射线:一个点+一个方向向量。

  线段:两个端点。

  多边形:按逆时针排序的点集。

  圆:圆心+半径。

  点积:两个向量的数量积。

  叉积:两个向量组成的四边形的有向面积。

 

基础部分有这些差不多了。比起解析式的运算过程来说,向量法的误差要小很多,而且基本不需要考虑特殊情况。

 

下面举个例子吧,分析解析几何和计算几何的差别。

 

最普通的,判断两直线是否相交或者重合,如果相交就计算交点。

  解析法:通过给定的四个点分别计算出每条直线的解析式,列二元一次方程组求解交点坐标。同时需要考虑到直线与坐标轴平行的特殊情况。

  向量法:用向量法表示出两条直线,若两向量叉积为0则向量共线,直线平行或重合。

  设交点P = A+tu,相当于A沿着向量走了t步,u是a直线的方向向量。既然P一定在b直线上,那么向量PC与向量v一定共线,叉积为0。解一元方程求解即可。

 

向量还是比较方便的。下面丢一点题目吧。

 

【POJ1269】

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <cmath>
 6 #define eps 1e-6
 7 using namespace std;
 8 
 9 int i,j,n,m,k,T;
10 
11 struct node
12 {
13     int x,y;
14     node(){}
15     node(int a,int b) { x = a,y = b; }
16 };
17 struct line { node po,ve; }l1,l2;
18 
19 int cj(node a,node b) { return a.x*b.y-a.y*b.x; }
20 
21 void getline(line &x)
22 {
23     int a,b,c,d;
24     scanf("%d %d %d %d",&a,&b,&c,&d);
25 
26     x.po = node(a,b);
27     x.ve = node(c-a,d-b);
28 }
29 
30 int main()
31 {
32     scanf("%d",&T);
33     printf("INTERSECTING LINES OUTPUT\n");
34     while (T--)
35     {
36         getline(l1); getline(l2);
37 
38         if (l1.ve.x * l2.ve.y == l1.ve.y * l2.ve.x)
39         {
40             node s( l1.po.x-l2.po.x , l1.po.y-l2.po.y );
41             if ( cj(s,l1.ve) == 0 ) printf("LINE\n");
42             else printf("NONE\n");
43         }
44         else{
45             double fm = ( (l1.po.x-l2.po.x)*l2.ve.y - (l1.po.y-l2.po.y)*l2.ve.x );
46             double fz = (l1.ve.x * l2.ve.y - l1.ve.y * l2.ve.x );
47 
48             double t = - fm / fz;
49 
50             double ansx = l1.po.x + t * l1.ve.x, ansy = l1.po.y + t * l1.ve.y;
51 
52             printf("POINT %.2lf %.2lf\n",ansx,ansy);
53         }
54 
55     }
56     printf("END OF OUTPUT\n");
57 
58     return 0;
59 }
POJ 1269

  上面说到的直线相交,入门题。

 

【POJ2318】题意:n条线段将一个矩形分成n+1块,计算每一个块内有几个点。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <iostream>
 5 using namespace std;
 6 
 7 struct node
 8 {
 9     int x,y;
10     node(){}
11     node(int a,int b) { x=a,y=b; }
12 };
13 struct Line{ node po,ve; } L[5005];
14 int ans[5005];
15 int i,j,n,m,k,x1,x2,y1,y2;
16 
17 int cj(node a,node b) { return a.x*b.y-a.y*b.x; }
18 
19 int main()
20 {
21     while ( scanf("%d %d %d %d %d %d",&n,&m,&x1,&y1,&x2,&y2) != EOF )
22     {
23         if (n == 0) break;
24         memset(ans,0,sizeof(ans));
25 
26         int a,b;
27         for (i = 1; i <= n; i++)
28         {
29             scanf("%d %d",&a,&b);
30             L[i].po = node(b,y2);
31             L[i].ve = node(a-b,y1-y2);
32         }
33 
34         for (i = 1; i <= m; i++)
35         {
36             scanf("%d %d",&a,&b);
37             int l = 1, r = n + 1;
38             while (l < r)
39             {
40                 int mid = (l + r) >> 1;
41                 node s( a-L[mid].po.x,b-L[mid].po.y );
42                 if ( cj(s,L[mid].ve) > 0 ) l = mid+1;
43                 else r = mid;
44             }
45             ans[l-1]++;
46         }
47 
48         for (i = 0; i <= n; i++) printf("%d: %d\n",i,ans[i]);
49         printf("\n");
50     }
51 
52     return 0;
53 }
POJ 2318

 

  叉积有一个性质:用a叉积b时,若符号为正,则a在b的顺时针方向,否则在逆时针方向。

  其实还是挺好做的,二分一下然后用叉积判断一下方向。O(nlogn)是可行的。

 

 

【POJ2653】题意:给定一些线段,按顺序插入然后判断最后有几条线段没有被盖住,保证答案<=1000。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <cmath>
 6 using namespace std;
 7 
 8 struct node
 9 {
10        double x,y;
11        node() {};
12        node (double a,double b) { x = a,y = b; }
13 };
14 struct Line { node p1,p2; int num; } L[100005];
15 int empty[100005];
16 int i,j,n,m,k,top;
17 
18 void clean()
19 {
20      int ntop = 0;
21      for (int i = 1; i <= top; i++)
22      {
23          if (empty[i]) { empty[i] = 0; continue; }
24          L[++ntop] = L[i];
25      }
26      top = ntop;
27 }
28 
29 bool cmp(Line a,Line b) { return a.num < b.num; }
30 double cj(node a,node b) { return a.x*b.y-a.y*b.x; }
31 void getpoint(node &x)
32 {
33      double a,b;
34      scanf("%lf %lf",&a,&b);
35      x.x = a; x.y = b;
36 }
37 
38 bool check(Line L1,Line L2) // L1 is a line and L2 is a segment
39 {
40      node s1( L2.p1.x-L1.p1.x , L2.p1.y-L1.p1.y );
41      node s2( L2.p2.x-L1.p1.x , L2.p2.y-L1.p1.y );
42      
43      node Ex( L1.p2.x-L1.p1.x , L1.p2.y-L1.p1.y );
44      
45      if ( cj( s1,Ex ) * cj( s2,Ex ) > 0 ) return 0;
46      return 1;
47 }
48 
49 int main()
50 {
51     node a,b;
52     while ( scanf("%d",&n),n != 0 )
53     {
54           top = 0;
55           for (i = 1; i <= n; i++)
56           {
57               getpoint(a); getpoint(b);
58               
59               Line t; t.p1 = a; t.p2 = b;
60               for (j = 1; j <= top; j++)
61                   if ( check(t,L[j]) && check(L[j],t) ) empty[j] = 1;
62               
63               L[++top] = t,L[top].num = i;
64               if (top >= 1000) clean();
65           }
66           clean();
67           
68           sort(L+1,L+1+top,cmp);
69           printf("Top sticks: %d",L[1].num);
70           for (i = 2; i <= top; i++)
71               printf(", %d",L[i].num);
72           
73           printf(".\n");
74     }
75     
76     return 0;
77 }
POJ 2653

 

  用一个数组维护当前在顶端的线段,每次插入一条线段的时候对于这个数组更新,若新线段能盖住某条线段则删除该条线段。

  不过不知道为什么我跑得挺慢的。

 

 

【POJ1556】题意:在一个10*10的矩形中有一些墙,墙不能通过,求从(0,5)到(10,5)的最短路径。

 

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <cmath>
  6 #include <queue>
  7 #define inf 1000000000
  8 using namespace std;
  9 
 10 queue<int> Q;
 11 
 12 struct node
 13 {
 14     double x,y;
 15     node (){}
 16     node (double a,double b) { x = a, y = b; }
 17 }P[10005];
 18 struct nd
 19 {
 20     int y,next; double c;
 21     nd() {}
 22     nd (int A,double B,int C) { y = A,c = B,next = C; }
 23 } h[10500];
 24 struct Line { node p1,p2; } L[10500];
 25 int eh[10005],vis[10005];
 26 int i,j,n,m,k,cnt,num,tot;
 27 double d[10050],x,y;
 28 
 29 double cj(node a,node b) { return a.x * b.y - a.y * b.x; }
 30 
 31 bool check(Line L1,Line L2) // L1 is a Line and L2 is a segment
 32 {
 33     node s1( L2.p1.x-L1.p1.x,L2.p1.y-L1.p1.y );
 34     node s2( L2.p2.x-L1.p1.x,L2.p2.y-L1.p1.y );
 35 
 36     node Ex( L1.p2.x-L1.p1.x,L1.p2.y-L1.p1.y );
 37 
 38     if ( cj(s1,Ex) * cj(s2,Ex) >= 0 ) return 0;
 39     return 1;
 40 }
 41 
 42 bool work(node A,node B)
 43 {
 44     Line t;
 45     t.p1 = A; t.p2 = B;
 46     for (int k = 1; k <= num; k++)
 47         if ( check(t,L[k]) && check(L[k],t) ) return 0;
 48     return 1;
 49 }
 50 
 51 void add(int a,int b)
 52 {
 53     double dit = (P[a].x-P[b].x)*(P[a].x-P[b].x)+(P[a].y-P[b].y)*(P[a].y-P[b].y);
 54     dit = sqrt(dit);
 55 
 56     h[cnt] = nd( b,dit,eh[a] ); eh[a] = cnt++;
 57     h[cnt] = nd( a,dit,eh[b] ); eh[b] = cnt++;
 58 }
 59 
 60 void spfa()
 61 {
 62     for (int i = 0; i <= tot; i++) d[i] = inf;
 63     d[0] = 0; Q.push(0);
 64     while (!Q.empty())
 65     {
 66         int now = Q.front(); Q.pop(); vis[now] = 0;
 67         for (int i = eh[now]; i + 1; i = h[i].next)
 68         {
 69             int y = h[i].y;
 70             if (d[y] > d[now] + h[i].c)
 71             {
 72                 d[y] = d[now]+h[i].c;
 73                 if (!vis[y]) vis[y] = 1,Q.push(y);
 74             }
 75         }
 76     }
 77 }
 78 
 79 int main()
 80 {
 81     while ( scanf("%d",&n),n != -1 )
 82     {
 83         memset(eh,-1,sizeof(eh)); cnt = num = tot = 0;
 84         for (i = 1; i <= n; i++)
 85         {
 86             scanf("%lf",&x);
 87             for (j = 1; j <= 4; j++)
 88             {
 89                 scanf("%lf",&y);
 90                 P[++tot] = node(x,y);
 91             }
 92             L[++num].p1 = node(x,0); L[num].p2 = P[tot-3];
 93             L[++num].p1 = P[tot-2]; L[num].p2 = P[tot-1];
 94             L[++num].p1 = P[tot]; L[num].p2 = node(x,10);
 95         }
 96 
 97         P[0] = node(0,5); P[++tot] = node(10,5);
 98 
 99         for (i = 0; i <= tot; i++)
100             for (j = i+1; j <= tot; j++)
101                 if ( work(P[i],P[j]) ) add(i,j);
102 
103         spfa();
104         printf("%.2lf\n",d[tot]);
105     }
106 
107     return 0;
108 }
POJ 1556

 

 

 

  枚举所有点对,判断是否可行,若可行则连边。然后跑一个SPFA算最短路就行了。

 

 

【POJ2007】题意:给一个凸包,要求极角排序后输出。

  极角排序:以原点为基准,按逆时针将点排序。

  方法:用叉积判断方向,可以确定两个向量的相对位置,就可以用来写cmp函数了。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <cmath>
 6 using namespace std;
 7 
 8 struct node
 9 {
10     int x,y;
11     node (){}
12     node (int a,int b) : x(a),y(b) {}
13 } P[105];
14 int i,j,n,m,k,top,x,y;
15 
16 int cj(node a,node b) { return a.x*b.y-a.y*b.x; }
17 bool cmp(node a,node b) { return cj(a,b) > 0; }
18 node operator-(node a,node b) { return node(a.x-b.x,a.y-b.y); }
19 
20 int main()
21 {
22     while ( scanf("%d %d",&x,&y) != EOF ) { i++; P[i] = node(x,y); }
23 
24     n = i;
25     stable_sort(P+1,P+1+n,cmp);
26     for (i = 1; i <= n; i++)
27         printf("(%d,%d)\n",P[i].x,P[i].y);
28 
29     return 0;
30 }
POJ 2007

 

【POJ3304】题意:给定n条直线,判断是否有一条直线能让所有直线投影到这条直线上后有公共点。

  转换一下题面,其实是要求是否有一条直线与所有直线相交。因为如果有这样的直线,那么这条直线的垂线肯定是符合题面要求的直线。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <iostream>
 6 #define eps 1e-8
 7 using namespace std;
 8 
 9 struct node
10 {
11     double x,y;
12     node(){}
13     node (double a,double b) { x = a,y = b; }
14 }P[205];
15 struct Line { node po,ve; } L[105];
16 int i,j,n,m,k,num,T;
17 
18 double cj(node a,node b) { return a.x*b.y-a.y*b.x; }
19 void getpoint(node &x)
20 {
21     double a,b;
22     scanf("%lf %lf",&a,&b);
23     x = node(a,b);
24 }
25 
26 bool check(Line X,Line Z)
27 {
28     node s1( Z.po.x-X.po.x,Z.po.y-X.po.y );
29     node s2( Z.ve.x-X.po.x,Z.ve.y-X.po.y );
30 
31     if ( cj(s1,X.ve) * cj(s2,X.ve) <= 0 ) return 1;
32     return 0;
33 }
34 
35 bool work()
36 {
37     for (i = 1; i <= num; i++)
38         for (j = i+1; j <= num; j++)
39         {
40             node a = P[i], b = P[j];
41 
42             if ( fabs(a.x-b.x) < eps && fabs(a.y-b.y) < eps ) continue;
43 
44             Line S;
45             S.po = a; S.ve = node( b.x-a.x,b.y-a.y );
46 
47             int find = 1;
48             for (k = 1; k <= n; k++)
49                 if ( !check(S,L[k]) ) { find = 0; break; }
50             if (!find) continue;
51 
52             return 1;
53         }
54     return 0;
55 }
56 
57 int main()
58 {
59     scanf("%d",&T);
60     while (T--)
61     {
62         scanf("%d",&n); num = 0;
63         for (i = 1; i <= n; i++)
64         {
65             node a,b;
66             getpoint(a); getpoint(b);
67             L[i].po = a; L[i].ve = b;
68             P[++num] = a; P[++num] = b;
69         }
70 
71         if ( work() ) printf("Yes!\n"); else printf("No!\n");
72     }
73 
74     return 0;
75 }
POJ 3304

 

 

 

暂时先这么多。

转载于:https://www.cnblogs.com/Tsuki/p/4245591.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值