POJ 3354 计算几何|平面多边形

poj.org/problem?id=3354

其实是恨水的题,我这种老年人水平1个小时左右可以完成,不是太明白poj上这个为什么做的人那么少,而且AC比例居然将近10%

Total Submissions: 203Accepted: 25

这个题意比较清晰,基本上就是遍历平面内所有的简单多边形,要注意去掉最外测的那个face

也不需要考虑异常情况,所以大致思路是

1. 每个多边形以顺时针方向访问边

2. 每条边的两个端点设为s,e, 使用这条边的多边形只有两个,因而可以用2个bit来标示改边对应的多边形是否遍历过

3. 每个定点的链接的所有边进行排序,按照与x轴的夹角的大小,这样当顺时针搜索某一个多边形的过程中通过某个边到达这个顶点时,假设这个边在这个顶点的链接边数组的索引是i, 那么当前多边形使用的下一个边的索引就是(i+1)%n (n是这个顶点的所有邻接边的个数);这里除了排序外,需要建立反向索引一边快速计算出i

最后就遍历每个边,如果这条边的某测没有访问过,这选择边的某个端点为起点,使得初始方向为顺时针方向,然后按照3中的思路依次寻找下一个边,知道遇到一个被访问的边,这时就找到了一个完整的多边形

边界: 在上述找到的所有多边形中必然会有一个是外边界,需要剔除掉,这是只需要标记边界上的任意一条边即可,如果某个多边形包含这个边,就忽略掉;而找到这个边也很容易,最下最左的那个点一定在边界上,并且这个点的排序后的邻接边的第一个边一定也在边界上,这个边的顺时针方向就在外边界上

实现

定点的邻接边的排序

typedef struct { int e, dx, dy; } NNode;
char plan(int x, int y) {
    if (y==0&&x>0) return 0;
    if (y>0&&x>0) return 1;
    if (x==0&&y>0) return 2;
    if (x<0&&y>0) return 3;
    if (y==0&&x<0) return 4;
    if (x<0&&y<0) return 5;
    if (x==0&&y<0) return 6;
    if (x>0&&y<0) return 7;
    assert(0);
    return -1;
}
int mycmp(const void *a, const void *b) {
    const NNode* pa = (const NNode*)a;
    const NNode* pb = (const NNode*)b;
    int x1=pa->dx, y1=pa->dy;
    int x2=pb->dx, y2=pb->dy;
    int k1=plan(x1, y1);
    int k2=plan(x2, y2);
    if (k1!=k2) return k1-k2;
    if (k1==0) return x1-x2;
    if (k1==4) return x2-x1;
    if (k1==2) return y1-y2;
    if (k1==6) return y2-y1;
    return y1*x2-y2*x1;
}

主函数

        scanf("%d", &n); if (n==0) break;
        memset(ix, 0xff, sizeof(ix));
        minx=miny=-1; c=0;
        k=0; for (i=0; i<n; i++) {
            scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
            x1+=100; y1+=100; x2+=100; y2+=100;
            if (x1<0||x1>200||y1<0||y1>200||x2<0||x2>200||y2<0||y2>200) assert(0);
            if (ix[x1][y1]==-1) { nei[k]=-1; ps[k].x=x1; ps[k].y=y1; ix[x1][y1]=k++; }
            if (ix[x2][y2]==-1) { nei[k]=-1; ps[k].x=x2; ps[k].y=y2; ix[x2][y2]=k++; }
            k1=ix[x1][y1]; k2=ix[x2][y2];
            if (minx==-1) { minx=maxx=x1; miny=maxy=y1; }
            if (minx>x1) minx=x1; if (minx>x2) minx=x2;
            if (maxx<x1) maxx=x1; if (maxx<x2) maxx=x2;
            if (miny>y1) miny=y1; if (miny>y2) miny=y2;
            if (maxy<y1) maxy=y1; if (maxy<y2) maxy=y2;
            es[i].s=k1; es[i].e=k2; es[i].m=0;
            pool[c].e=i; pool[c].n=nei[k1]; nei[k1]=c++;
            pool[c].e=i; pool[c].n=nei[k2]; nei[k2]=c++;
        }
        if (ix[minx][miny]==-1) assert(0);
        if (ix[minx][maxy]==-1) assert(0);
        if (ix[maxx][miny]==-1) assert(0);
        if (ix[maxx][maxy]==-1) assert(0);
        p=ebuf; for (i=0; i<k; i++) {
            x1=ps[i].x; y1=ps[i].y;
            c=0; j=nei[i]; while(j!=-1) {
                e=pool[j].e; j=pool[j].n;
                k2=es[e].s; if (k2==i) k2=es[e].e;
                x2=ps[k2].x; y2=ps[k2].y;
                rds[c].e=e; rds[c].dx=x2-x1; rds[c].dy=y2-y1; c++;
            } qsort(rds, c, sizeof(rds[0]), mycmp);
            for (j=0; j<c; j++) {
                e=rds[j].e; p[j]=e;
                if (es[e].s==i) es[e].ex=j;else es[e].sx=j;
            }
            nc[i]=c; np[i]=p; p+=c;
        }
        k1=ix[minx][miny];
        me=np[k1][0]; if (es[e].s==k1) mm=0; else mm=1;
        rc=0;
        for (i=0; i<k; i++) {
            for (j=0; j<nc[i]; j++) {
                e=np[i][j]; if (i==es[e].s) m=1; else m=2;
                if (es[e].m&m) continue;
                k1=i; if (m==1) k2=es[e].e; else k2=es[e].s;
                cc=0;
                bd=0;
                while(1) {
                    if (es[e].m&m) break;
                    if (e==me&&mm==m) bd=1;
                    es[e].m |= m;
                    cc++;
                    if (k1==es[e].s) ni=(es[e].sx+1)%nc[k2];
                    else ni=(es[e].ex+1)%nc[k2];
                    ne=np[k2][ni];
                    k1=k2; if (k2==es[ne].s) { m=1; k2=es[ne].e; } else { m=2; k2=es[ne].s; }
                    e=ne;
                }
                if (bd==0&&rc<cc) rc=cc;
            }
        }
        printf("%d\n", rc);

思路清晰,代码也不复杂,真就1A了,而且因为内存使用少,目前排第一 (主要是因为C++比G++少占很多内存,hoho~)


 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值