USACO section 3.4 Closed Fences(计算几何+叉积+二分)

Closed Fences

A closed fence in the plane is a set of non-crossing, connected line segments with N corners (3 < N < 200). The corners or vertices are each distinct and are listed in counter-clockwise order in an array {xi, yi}, i in (1..N).

Every pair of adjacent vertices defines a side of the fence. Thus {xi yi xi+1 yi+1} is a side of the fence for all i in (1..N). For our purposes, N+1 = 1, so that the first and last vertices making the fence closed.

Here is a typical closed fence and a point x,y:

                         * x3,y3
                 x5,y5  / \
    x,y *          *   /   \
                  / \ /     \
                 /   *       \
           x6,y6*   x4,y4     \
                |              \
                |               \
           x1,y1*----------------* x2,y2

Write a program which will do the following:

  • Test an ordered list of vertices {xi,yi}, i in (1..N) to see if the array is a valid fence.
  • Find the set of fence sides that a person (with no height) who is standing in the plane at position (x,y) can "see" when looking at the fence. The location x,y may fall anywhere not on the fence.

A fence side can be seen if there exists a ray that connects (x,y) and any point on the side, and the ray does not intersect any other side of the fence. A side that is parallel to the line of sight is not considered visible. In the figure, above the segments x3,y3-x4,y4; x5,y5-x6,y6; and x6-y6-x1,y1 are visible or partially visible from x,y.

PROGRAM NAME: fence4

INPUT FORMAT

Line 1: N, the number of corners in the fence
Line 2: Two space-separated integers, x and y, that are the location of the observer. Both integers will fit into 16 bits.
Line 3-N+2: A pair of space-separated integers denoting the X,Y location of the corner. The pairs are given in counterclockwise order. Both integers are no larger than 1000 in magnitude.
NOTE: I have added anNew test case #12 for this task. Let me know if you think it's wrong. Rob Be sure to include USACO in your mail subject!

SAMPLE INPUT (file fence4.in)

13
5 5
0 0
7 0
5 2
7 5
5 7
3 5
4 9
1 8
2 5
0 9
-2 7
0 3
-3 1 

OUTPUT FORMAT

If the sequence is not a valid fence, the output is a single line containing the word "NOFENCE".

Otherwise, the output is a listing of visible fence segments, one per line, shown as four space-separated integers that represent the two corners. Express the points in the segment by showing first the point that is earlier in the input, then the point that is later. Sort the segments for output by examining the last point and showing first those points that are earlier in the input. Use the same rule on the first of the two points in case of ties.

SAMPLE OUTPUT (file fence4.out)

7
0 0 7 0
5 2 7 5
7 5 5 7
5 7 3 5
-2 7 0 3
0 0 -3 1
0 3 -3 1

思路:直接枚举每条边,判断这条边是否能看见,判断方法是:从人所在的位置e 分别与需要判段的边的两端连线,然后判断这两条线是否与其他的边同时非严格相交(可交与边界点),如果是,则这条边肯定被挡住了,否则在看是否有一条连线与其严格相交,有就将这条边砍成两段,继续判断,只要砍出一段不被挡住,这条边就不算被挡住,如果砍到很短的值还找不到,那就算被挡住了。

吐槽下:这道题卡了整一个多月,当初不咋会几何,而且,还得用二分判断特殊的情况,这题确实挺难,或许在大神眼中就是道水水吧。

/*
ID:nealgav1
LANG:C++
PROG:fence4
*/
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<cmath>
using namespace std;
const double ex=1e-10;
const int mm=2100;
class _point
{
  public:double x,y;
  _point(double xx,double yy){x=xx;y=yy;}
  _point(){}
};
class _line
{
  public:_point s,t;
  _line(_point ss,_point tt){s=ss;t=tt;}
  _line(){}
};
inline double ma(double x,double y)
{
  if(x<y)return y;return x;
}
inline double mi(double x,double y)
{
  return x<y?x:y;
}
double dis(_point a,_point b)
{
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline double aabs(double x)
{
  return x>0?x:-x;
}
///r>0 op在sp-tp的顺时针
double mul(_point sp,_point tp,_point op)///向量叉积
{
  return ((tp.x-sp.x)*(op.y-sp.y)-(tp.y-sp.y)*(op.x-sp.x));
}
bool online(_line a,_point b)
{
  return (aabs(mul(a.s,a.t,b))<=ex&&(a.s.x-b.x)*(a.t.x-b.x)<=ex&&(a.s.y-b.y)*(a.t.y-b.y)<=ex);
}
bool intersect(_line a,_line b)
{
  return (ma(a.s.x,a.t.x)>=mi(b.s.x,b.t.x)&&ma(b.s.x,b.t.x)>=mi(a.s.x,a.t.x)&&
          ma(a.s.y,a.t.y)>=mi(b.s.y,b.t.y)&&ma(b.s.y,b.t.y)>=mi(a.s.y,a.t.y)&&///排斥实验
          mul(a.s,a.t,b.s)*mul(a.s,a.t,b.t)<=0&&mul(b.s,b.t,a.s)*mul(b.s,b.t,a.t)<=0);///跨立实验
}
bool a_intersect(_line a,_line b)
{
  return (intersect(a,b)&&(!online(a,b.s))&&(!online(a,b.t))&&(!online(b,a.s))&&(!online(b,a.t)));
}
_point midpoint(_point a, _point b)
{
    return _point((a.x + b.x)/2.0, (a.y + b.y)/2.0);
}
int n;
_point p[mm],e;_line li[mm];
bool vis[mm];
bool save(int k, _line a)
{
    if(dis(a.s,a.t)<1e-5)return false;///切到足够小了,直接认为看不到
    int flag=0;
    for(int v=1;v<=n;v++)
        if (v!= k){
            ///一边的左右两点同时被挡住,说明一定是看不见
            if (intersect(_line(e, a.s),li[v])&&intersect(_line(e, a.t),li[v]))
              {
                flag = 1;
                break;
              }
            ///有一个是被挡住,则需要切段判断
            if(a_intersect(_line(e, a.s),li[v])||a_intersect(_line(e, a.t), li[v]))///不交在点上
            flag = 2;
        }
    if (flag == 0) return true;
    if (flag == 1) return false;
    else return save(k, _line(a.s, midpoint(a.s, a.t))) || save(k, _line(midpoint(a.s, a.t), a.t));
}
int main()
{
    freopen("fence4.in", "r", stdin);
    freopen("fence4.out", "w", stdout);
    scanf("%d", &n);
    scanf("%lf%lf", &e.x, &e.y);
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf", &p[i].x, &p[i].y);
    for (int i = 1; i < n; i++)
        li[i] = _line(p[i], p[i + 1]);
    li[n]=_line(p[1], p[n]);
    int cnt = 0;
    memset(vis,0, sizeof(vis));
    for (int i = 1; i <= n; i++)
        if (save(i,li[i]))
        {
            cnt++;
            vis[i]=1;
        }
    printf("%d\n", cnt);
    for (int i = 1; i < n - 1; i++)
        if (vis[i]) printf("%d %d %d %d\n", (int)li[i].s.x, (int)li[i].s.y, (int)li[i].t.x, (int)li[i].t.y);
    if (vis[n]) printf("%d %d %d %d\n", (int)li[n].s.x, (int)li[n].s.y, (int)li[n].t.x, (int)li[n].t.y);
    if (vis[n - 1]) printf("%d %d %d %d\n", (int)li[n - 1].s.x, (int)li[n - 1].s.y, (int)li[n - 1].t.x, (int)li[n - 1].t.y);
    return 0;
}





Executing...
   Test 1: TEST OK [0.000 secs, 3336 KB]
   Test 2: TEST OK [0.000 secs, 3336 KB]
   Test 3: TEST OK [0.000 secs, 3336 KB]
   Test 4: TEST OK [0.000 secs, 3336 KB]
   Test 5: TEST OK [0.000 secs, 3336 KB]
   Test 6: TEST OK [0.011 secs, 3336 KB]
   Test 7: TEST OK [0.032 secs, 3336 KB]
   Test 8: TEST OK [0.011 secs, 3336 KB]
   Test 9: TEST OK [0.086 secs, 3336 KB]
   Test 10: TEST OK [0.097 secs, 3336 KB]
   Test 11: TEST OK [0.000 secs, 3336 KB]
   Test 12: TEST OK [0.000 secs, 3336 KB]

  
   Closed Fences
Hal Burch
  

Determining if the fence is simple is, um, simple. For each pair of segments which don't share a vertex, determine if they intersect. If they do, the fence isn't simple. Otherwise, it is.

Let p be the point given. Given another point q and a segment e, using the techniques described in the computation geometry module, we can determine the following:

  1. Whether the ray pq intersects e
  2. If it does, how far along that ray the intersection takes place

Thus, we can determine the first segment intersected by the ray, which must be visible to the point. By checking all points qin the plane, we could determine all the visible faces. The problem with this is, of course, that checking all points q is impossible.

Instead, check for all endpoints of segments and all midpoints of segments. If an edge e is visible, then either its midpoint is visible, or some set of edges obscure it. If some set of edges obscure e, then there is some edge where e is visible 'just beyond' one of its endpoints. In this case, if we do our intersection test such that 'brushing' an endpoint of a segment does not count as intersecting it, then the ray from the observer to that endpoint will intersect the edge e first.

Thus, for each endpoint and midpoints, we determine the first edge intersected, and mark it as visible.

/*
PROB: fence4
ID: hburch002
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define SQR(A) ((A)*(A))

/* maximum number of points */
#define MAXN 201

/* number of points */
int npos;

/* the points, where pos[npos] == pos[0] */
double pos[MAXN][2];

/* observer's location */
double obsx, obsy;

/* cansee[x] = can we see the segment (x,x+1)? */
int cansee[MAXN];

int side(double sx, double sy, double ex, double ey, int p)
 { /* determine the side that the points lie on */
  double dx, dy;
  double px, py;
  double t;

  dx = ex - sx;
  dy = ey - sy;

  px = pos[p][0] - sx;
  py = pos[p][1] - sy;

  /* take cross-product */
  t = dx * py - dy * px;

  if (t > 0.00001) return 0; /* "left" side */
  if (t < -0.00001) return 1; /* "right" side */
  return 2; /* on the line */
 }

int first_inter(double sx, double sy, double ex, double ey)
 { /* what is the first segment intersected by the ray s->e */
  int lv; /* loop variable */
  int t1, t2;
  int s1, s2;
  double ax, ay, bx, by;
  double t;
  double coeff, cnst;
  double i, j;
  double x, y;
  double mlbrush, mrbrush; /* when is the earliest brush on a side? */

  /* min = distance to nearest intersection point */
  /* mloc = edge where this occurs */
  double min = 1e10; /* ~= infinity */
  int mloc = npos; /* unused location */

  mlbrush = mrbrush = 1e10; /* infinity */

  for (lv = 0; lv < npos; lv++)
   { /* for each segment, determine length along */
    ax = pos[lv][0];
    ay = pos[lv][1];
    bx = pos[lv+1][0];
    by = pos[lv+1][1];

    /* take cross product */
    t = (ex - sx) * (ay - by) - (ey - sy) * (ax - bx);
    if (t > -0.00001 && t < 0.00001) /* parallel */
      continue; /* not considered visible */

    /* not parallel */
    /* we are now solving the following equations:
     (ex - sx) j + sx = (bx - ax) i + ax
     (ey - sy) j + sy = (by - ay) i + ay
    */

    /* solves for alpha by multiple first by (by - ay) and
       the second by (bx - ax) and subtracting equations */
    cnst = (ax - sx)*(by - ay) - (ay - sy)*(bx - ax);
    coeff = (ex - sx) * (by - ay) - (ey - sy) * (bx - ax);
    if (coeff > -0.00001 && coeff < .00001)
     { /* degenerate, one of bx - ax and by - ay is about zero */
      if (bx - ax > -.00001 && bx - ax < 0.00001)
       { /* bx - ax == 0, can solve first eqn directly */
        cnst = ax - sx;
        coeff = ex - sx;
       } else { /* by - ay == 0, can solve second eqn directly */
        cnst = ay - sy;
        coeff = ey - sy;
       }
     }
    j = cnst / coeff;

    /* if intersection occurs before starting point, no intersection */
    if (j < -.00001) continue;

    /* determine beta */
    cnst = sx + (ex - sx) * j - ax;
    coeff = bx - ax;
    if (coeff > -0.00001 && coeff < .00001)
     { /* handle degeneracy */
      cnst = sy + (ey - sy) * j - ay;
      coeff = by - ay;
     }
    i = cnst / coeff;

    /* if the interesection occurs with i < 0 | i > 1, the
       intersection is not within the confines of the segment */
    if (i < -.00001 || i > 1.00001) continue;

    /* calculate intersection point */
    x = ax + (bx - ax) * i;
    y = ay + (by - ay) * i;

    /* determine distance along line that intersection occurs */
    t = (x - sx) * (ex - sx) + (y - sy) * (ey - sy);

    /* make sure it's in bounds, and better than what we have */
    if (t < -0.00001 || t > min) continue;
    
    /* if it occurs at an end point */
    if (i < .00001 || i > 0.99999) 
     {
      /* find the endpoints that are incident to the intersected endpoint */
      if (i < .00001)
       {
        t1 = lv-1;
        if (t1 < 0) t1 += npos;
        t2 = lv+1;
       } else {
        t1 = lv;
        t2 = lv+2;
        if (t2 >= npos) t2 -= npos;
       }

      /* if they lie on the same side of the line, then ray 'brushes'
         endpoint, which is not considered to an intersection */

      s1 = side(sx,sy,ex,ey,t1);
      s2 = side(sx,sy,ex,ey,t2);
      if (s1 == s2) {
	if (s1 == 0 && t < mlbrush) mlbrush = t;
	if (s1 == 1 && t < mrbrush) mrbrush = t;
        continue;
      }
     }
    /* found a better edge! */
    min = t;
    mloc = lv;
   }
/* if it brushes on both sides, it cannot be seen */
  if (min > mlbrush && min > mrbrush) return npos;
  return mloc;
 }

int check_intersect(int f1, int f2) 
 { /* do (f1,f1+1) and (f2,f2+1) intersect? */
  double sx, sy;
  double ex, ey;
  
  sx = pos[f1][0];
  sy = pos[f1][1];
  ex = pos[f1+1][0];
  ey = pos[f1+1][1];

  if (side(sx, sy, ex, ey, f2) == side(sx, sy, ex, ey, f2+1))
  /* are the f2 and f2+1 on the same side of (f1,f1+1)? */
    return 0; /* if so, the segments don't intersect */

  sx = pos[f2][0];
  sy = pos[f2][1];
  ex = pos[f2+1][0];
  ey = pos[f2+1][1];

  if (side(sx, sy, ex, ey, f1) == side(sx, sy, ex, ey, f1+1))
  /* are f1 & f1+1 on the same side of (f2,f2+1) */
    return 0; /* if so, the segments don't intersect */

  /* the endpoints of each segment are on opposite sides of
     the other segment.  Therefore, they intersect */
  return 1; 
 }

int main(int argc, char **argv)
 {
  FILE *fout, *fin;
  int lv, lv2;
  int cnt;
  int t;
  double dx, dy;

  if ((fin = fopen("fence4.in", "r")) == NULL)
   {
    perror ("fopen fin");
    exit(1);
   }
  if ((fout = fopen("fence4.out", "w")) == NULL)
   {
    perror ("fopen fout");
    exit(1);
   }

  fscanf (fin, "%d", &npos);
  fscanf (fin, "%lf %lf", &obsx, &obsy);
  for (lv = 0; lv < npos; lv++)
    fscanf (fin, "%lf %lf", &pos[lv][0], &pos[lv][1]);
  pos[npos][0] = pos[0][0];
  pos[npos][1] = pos[0][1];

/* for each pair of segments that don't share a vertex */
  for (lv = 0; lv < npos; lv++)
    for (lv2 = lv+2; lv2 < npos; lv2++)
      if (check_intersect(lv, lv2))
       { /* if they intersect */

        /* and don't share a vertex */
        if (lv == 0 && lv2 == npos-1) continue; 

        /* then the fence is invalid */
        fprintf (fout, "NOFENCE\n"); 
        return 0;
       }

  for (lv = 0; lv < npos; lv++)
   {
    /* check endpoint */
    cansee[first_inter(obsx, obsy, pos[lv][0], pos[lv][1])] = 1;

    /* check midpoint of segment (lv, lv+1) */
    cansee[first_inter(obsx, obsy, 
               (pos[lv][0] + pos[lv+1][0])*0.5, 
               (pos[lv][1] + pos[lv+1][1])*0.5)] = 1;
   }

  /* count number of visible segments */
  cnt = 0;
  for (lv = 0; lv < npos; lv++)
    if (cansee[lv]) cnt++;

  fprintf (fout, "%i\n", cnt);

  /* list visible segments */
  for (lv = 0; lv < npos-2; lv++)
    if (cansee[lv])
     {
      fprintf (fout, "%.0f %.0f %.0f %.0f\n", pos[lv][0], pos[lv][1], 
            pos[lv+1][0], pos[lv+1][1]);
     }
  /* because of the way the ordering is defined, these two must be
     checked separately */
  if (cansee[npos-1])
   {
    fprintf (fout, "%.0f %.0f %.0f %.0f\n", pos[0][0], pos[0][1], 
          pos[npos-1][0], pos[npos-1][1]);
   }
  if (cansee[npos-2])
   {
      fprintf (fout, "%.0f %.0f %.0f %.0f\n", pos[npos-2][0], pos[npos-2][1], 
            pos[npos-2+1][0], pos[npos-2+1][1]);
   }
   
  return 0;
 }

All tests OK.

YOUR PROGRAM ('fence4') WORKED FIRST TIME! That's fantastic-- and a rare thing. Please accept these special automatedcongratulations.

Here are the test data inputs:

------- test 1 ----
13
5 5
0 0
7 0
5 2
7 5
5 7
3 5
4 9
1 8
2 5
0 9
-2 7
0 3
-3 1
------- test 2 ----
4
1 1
0 0
2 0
2 2
0 2
------- test 3 ----
4
1 -10
0 0
2 0
2 2
0 2
------- test 4 ----
12
1 -10
0 0
1 0
2 0
3 0
3 1
3 2
3 3
2 3
1 3
0 3
0 2
0 1
------- test 5 ----
4
100 100
0 0
2 0
2 2
0 2
------- test 6 ----
------- test 7 ----
------- test 8 ----
199
-2 -10
0 0
1 1
0 1
1 2
0 2
1 3
0 3
1 4
0 4
1 5
0 5
1 6
0 6
1 7
0 7
1 8
0 8
1 9
0 9
1 10
0 10
1 11
0 11
1 12
0 12
1 13
0 13
1 14
0 14
1 15
0 15
1 16
0 16
1 17
0 17
1 18
0 18
1 19
0 19
1 20
0 20
1 21
0 21
1 22
0 22
1 23
0 23
1 24
0 24
1 25
0 25
1 26
0 26
1 27
0 27
1 28
0 28
1 29
0 29
1 30
0 30
1 31
0 31
1 32
0 32
1 33
0 33
1 34
0 34
1 35
0 35
1 36
0 36
1 37
0 37
1 38
0 38
1 39
0 39
1 40
0 40
1 41
0 41
1 42
0 42
1 43
0 43
1 44
0 44
1 45
0 45
1 46
0 46
1 47
0 47
1 48
0 48
1 49
0 49
1 50
0 50
1 51
0 51
1 52
0 52
1 53
0 53
1 54
0 54
1 55
0 55
1 56
0 56
1 57
0 57
1 58
0 58
1 59
0 59
1 60
0 60
1 61
0 61
1 62
0 62
1 63
0 63
1 64
0 64
1 65
0 65
1 66
0 66
1 67
0 67
1 68
0 68
1 69
0 69
1 70
0 70
1 71
0 71
1 72
0 72
1 73
0 73
1 74
0 74
1 75
0 75
1 76
0 76
1 77
0 77
1 78
0 78
1 79
0 79
1 80
0 80
1 81
0 81
1 82
0 82
1 83
0 83
1 84
0 84
1 85
0 85
1 86
0 86
1 87
0 87
1 88
0 88
1 89
0 89
1 90
0 90
1 91
0 91
1 92
0 92
1 93
0 93
1 94
0 94
1 95
0 95
1 96
0 96
1 97
0 97
1 98
0 98
-1 1
-1 0
------- test 9 ----
199
-1 10
0 0
2 1
0 1
2 2
0 2
2 3
0 3
2 4
0 4
2 5
0 5
2 6
0 6
2 7
0 7
2 8
0 8
2 9
0 9
2 10
0 10
2 11
0 11
2 12
0 12
2 13
0 13
2 14
0 14
2 15
0 15
2 16
0 16
2 17
0 17
2 18
0 18
2 19
0 19
2 20
0 20
2 21
0 21
2 22
0 22
2 23
0 23
2 24
0 24
2 25
0 25
2 26
0 26
2 27
0 27
2 28
0 28
2 29
0 29
2 30
0 30
2 31
0 31
2 32
0 32
2 33
0 33
2 34
0 34
2 35
0 35
2 36
0 36
2 37
0 37
2 38
0 38
2 39
0 39
2 40
0 40
2 41
0 41
2 42
0 42
2 43
0 43
2 44
0 44
2 45
0 45
2 46
0 46
2 47
0 47
2 48
0 48
2 49
0 49
2 50
0 50
2 51
0 51
2 52
0 52
2 53
0 53
2 54
0 54
2 55
0 55
2 56
0 56
2 57
0 57
2 58
0 58
2 59
0 59
2 60
0 60
2 61
0 61
2 62
0 62
2 63
0 63
2 64
0 64
2 65
0 65
2 66
0 66
2 67
0 67
2 68
0 68
2 69
0 69
2 70
0 70
2 71
0 71
2 72
0 72
2 73
0 73
2 74
0 74
2 75
0 75
2 76
0 76
2 77
0 77
2 78
0 78
2 79
0 79
2 80
0 80
2 81
0 81
2 82
0 82
2 83
0 83
2 84
0 84
2 85
0 85
2 86
0 86
2 87
0 87
2 88
0 88
2 89
0 89
2 90
0 90
2 91
0 91
2 92
0 92
2 93
0 93
2 94
0 94
2 95
0 95
2 96
0 96
2 97
0 97
2 98
0 98
-2 1
-2 0
------- test 10 ----
199
10 10
0 0
1 1
0 1
1 2
0 2
1 3
0 3
1 4
0 4
1 5
0 5
1 6
0 6
1 7
0 7
1 8
0 8
1 9
0 9
1 10
0 10
1 11
0 11
1 12
0 12
1 13
0 13
1 14
0 14
1 15
0 15
1 16
0 16
1 17
0 17
1 18
0 18
1 19
0 19
1 20
0 20
1 21
0 21
1 22
0 22
1 23
0 23
1 24
0 24
1 25
0 25
1 26
0 26
1 27
0 27
1 28
0 28
1 29
0 29
1 30
0 30
1 31
0 31
1 32
0 32
1 33
0 33
1 34
0 34
1 35
0 35
1 36
0 36
1 37
0 37
1 38
0 38
1 39
0 39
1 40
0 40
1 41
0 41
1 42
0 42
1 43
0 43
1 44
0 44
1 45
0 45
1 46
0 46
1 47
0 47
1 48
0 48
1 49
0 49
1 50
0 50
1 51
0 51
1 52
0 52
1 53
0 53
1 54
0 54
1 55
0 55
1 56
0 56
1 57
0 57
1 58
0 58
1 59
0 59
1 60
0 60
1 61
0 61
1 62
0 62
1 63
0 63
1 64
0 64
1 65
0 65
1 66
0 66
1 67
0 67
1 68
0 68
1 69
0 69
1 70
0 70
1 71
0 71
1 72
0 72
1 73
0 73
1 74
0 74
1 75
0 75
1 76
0 76
1 77
0 77
1 78
0 78
1 79
0 79
1 80
0 80
1 81
0 81
1 82
0 82
1 83
0 83
1 84
0 84
1 85
0 85
1 86
0 86
1 87
0 87
1 88
0 88
1 89
0 89
1 90
0 90
1 91
0 91
1 92
0 92
1 93
0 93
1 94
0 94
1 95
0 95
1 96
0 96
1 97
0 97
1 98
0 98
-1 1
-1 0
------- test 11 ----
7
1 3
1 1
-2 1
1 -4
3 2
1 2
2 0
0 0
------- test 12 ----
6
7 8
6 6
8 6
8 8
15 0
0 0
6 8

你可能感兴趣的:(USACO section 3.4 Closed Fences(计算几何+叉积+二分))