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

来源:互联网 发布:淘宝女鞋店铺推荐 编辑:程序博客网 时间:2024/05/17 21:51

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 fenceLine 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)

135 50 07 05 27 55 73 54 91 82 50 9-2 70 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)

70 0 7 05 2 7 57 5 5 75 7 3 5-2 7 0 30 0 -3 10 3 -3 1

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

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

/*ID:nealgav1LANG: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 FencesHal 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: fence4ID: 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 ----135 50 07 05 27 55 73 54 91 82 50 9-2 70 3-3 1------- test 2 ----41 10 02 02 20 2------- test 3 ----41 -100 02 02 20 2------- test 4 ----121 -100 01 02 03 03 13 23 32 31 30 30 20 1------- test 5 ----4100 1000 02 02 20 2------- test 6 ----------- test 7 ----------- test 8 ----199-2 -100 01 10 11 20 21 30 31 40 41 50 51 60 61 70 71 80 81 90 91 100 101 110 111 120 121 130 131 140 141 150 151 160 161 170 171 180 181 190 191 200 201 210 211 220 221 230 231 240 241 250 251 260 261 270 271 280 281 290 291 300 301 310 311 320 321 330 331 340 341 350 351 360 361 370 371 380 381 390 391 400 401 410 411 420 421 430 431 440 441 450 451 460 461 470 471 480 481 490 491 500 501 510 511 520 521 530 531 540 541 550 551 560 561 570 571 580 581 590 591 600 601 610 611 620 621 630 631 640 641 650 651 660 661 670 671 680 681 690 691 700 701 710 711 720 721 730 731 740 741 750 751 760 761 770 771 780 781 790 791 800 801 810 811 820 821 830 831 840 841 850 851 860 861 870 871 880 881 890 891 900 901 910 911 920 921 930 931 940 941 950 951 960 961 970 971 980 98-1 1-1 0------- test 9 ----199-1 100 02 10 12 20 22 30 32 40 42 50 52 60 62 70 72 80 82 90 92 100 102 110 112 120 122 130 132 140 142 150 152 160 162 170 172 180 182 190 192 200 202 210 212 220 222 230 232 240 242 250 252 260 262 270 272 280 282 290 292 300 302 310 312 320 322 330 332 340 342 350 352 360 362 370 372 380 382 390 392 400 402 410 412 420 422 430 432 440 442 450 452 460 462 470 472 480 482 490 492 500 502 510 512 520 522 530 532 540 542 550 552 560 562 570 572 580 582 590 592 600 602 610 612 620 622 630 632 640 642 650 652 660 662 670 672 680 682 690 692 700 702 710 712 720 722 730 732 740 742 750 752 760 762 770 772 780 782 790 792 800 802 810 812 820 822 830 832 840 842 850 852 860 862 870 872 880 882 890 892 900 902 910 912 920 922 930 932 940 942 950 952 960 962 970 972 980 98-2 1-2 0------- test 10 ----19910 100 01 10 11 20 21 30 31 40 41 50 51 60 61 70 71 80 81 90 91 100 101 110 111 120 121 130 131 140 141 150 151 160 161 170 171 180 181 190 191 200 201 210 211 220 221 230 231 240 241 250 251 260 261 270 271 280 281 290 291 300 301 310 311 320 321 330 331 340 341 350 351 360 361 370 371 380 381 390 391 400 401 410 411 420 421 430 431 440 441 450 451 460 461 470 471 480 481 490 491 500 501 510 511 520 521 530 531 540 541 550 551 560 561 570 571 580 581 590 591 600 601 610 611 620 621 630 631 640 641 650 651 660 661 670 671 680 681 690 691 700 701 710 711 720 721 730 731 740 741 750 751 760 761 770 771 780 781 790 791 800 801 810 811 820 821 830 831 840 841 850 851 860 861 870 871 880 881 890 891 900 901 910 911 920 921 930 931 940 941 950 951 960 961 970 971 980 98-1 1-1 0------- test 11 ----71 31 1-2 11 -43 21 22 00 0------- test 12 ----67 86 68 68 815 00 06 8

原创粉丝点击