• POJ 计算几何


    计算几何学

    几何公式

    poj1265(pick定理)

    叉积和点积的运用

    poj2031,poj1039

    多边型的简单算法和相关判定

    poj1408,poj1584

    凸包

    poj2187,poj1113

     POJ 1265

    这题貌似。。。pick定理+线段上的整数点的个数+叉积求多边形面积。。。

    pick定理:http://www.cnblogs.com/vongang/archive/2012/04/07/2435741.html

    线段上的整数点的个数:算导上的推论,方程ax ≡ c (mod b)或者对模n有d个不同的解,或则无解。 同余方程可写成 ax + by = c. 即是线段ab上有d个整数点。

    叉积求多边形面积:明白叉积的概念就很清楚了,设叉积 A = p0p1 × p0p2 。|A|表示平行平行四边形p0p1p0'p2的面积。如果A < 0 表示p1 在p2 的逆时针方向上。以某一个点为p0,则所求的多边形的面积就是沿顺时针方向两两相邻的所有叉积和。(网上有很多证明。)

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>

    using namespace std;

    const int MAXN = 110;

    struct pot {
    int x;
    int y;
    pot(int a = 0, int b = 0) : x(a), y(b) {}
    pot operator + (const pot b) {
    return pot(x + b.x, y + b.y);
    }
    }p[MAXN];

    int det(pot a, pot b) {
    return a.x * b.y - a.y * b.x;
    }

    int gcd(int a, int b) {
    if(b == 0) return a;
    return gcd(b, a % b);
    }

    int main() {
    //freopen("data.in", "r", stdin);

    int T, n, i, B, I, cas = 0;
    double area;
    pot u;
    scanf("%d", &T);
    while(T--) {
    scanf("%d", &n);
    p[0] = pot(0, 0);
    B = I = 0;
    for(i = 1; i <= n; ++i) {
    scanf("%d%d", &u.x, &u.y);
    B += abs(double(gcd(u.x, u.y)));
    p[i] = p[i-1] + u;
    }
    area = 0;

    for(i = 0; i <= n; ++i) {
    //printf("%d %d\n", p[i].x, p[i].y);
    area += det(p[i], p[i+1]);
    }

    area = abs(area)/2;
    I = area + 1 - B/2;

    printf("Scenario #%d:\n%d %d %.1lf\n", ++cas, I, B, area);
    if(T) printf("\n");
    }
    return 0;
    }


    POJ 2031

    这题看题目挺吓人的,还三维坐标系。其实就是求出各球之间的距离,然后prim求最小生成树。幸亏有这一句 you may consider that two corridors never intersect,要不然有得恶心了。。。

    渣代码:

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int MAXN = 110;
    const double eps = 1e-8;
    const double inf = 30010.0;
    
    struct node {
        double x;
        double y;
        double z;
        double r;
    } cir[MAXN];
    
    double mp[MAXN][MAXN];
    double low[MAXN];
    bool vis[MAXN];
    int n;
    
    double cmp(double x) {
        if(x > eps)    return 1;
        else if(x < -eps)    return -1;
        return 0;
    }
    
    double dis(node a, node b) {
        a.x -= b.x; a.y -= b.y; a.z -= b.z;
        double d = sqrt(a.x*a.x + a.y*a.y + a.z*a.z) - a.r - b.r;
        if(cmp(d) <= 0)    return 0;
        return d;
    }
    
    double prim() {
        int i, j, f;
        double sum = 0, min;
        for(i = 0; i < n; ++i) {
            low[i] = mp[0][i];
            vis[i] = false;
        }
        low[0] = 0; vis[0] = true;
        for(i = 1; i < n; ++i) {
            min = inf; f = 0;
            for(j = 1; j < n; ++j) {
                if(!vis[j] && min > low[j]) {
                    min = low[j]; f = j;
                }
            }
    
            if(f != 0)    sum += min;
            vis[f] = true;
    
            for(j = 1; j < n; ++j) {
                if(!vis[j] && low[j] - mp[f][j] > eps) {
                    low[j] = mp[f][j];
                }
            }
        }
        return sum;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i, j;
        double d, ans;
        while(scanf("%d", &n), n) {
            for(i = 0; i < n; ++i) {
                for(j = 0; j < n; ++j) {
                    if(i == j)    mp[i][j] = 0;
                    else    mp[i][j] = inf;
                }
            }
            for(i = 0; i < n; ++i) {
                scanf("%lf%lf%lf%lf", &cir[i].x, &cir[i].y, &cir[i].z, &cir[i].r);
                for(j = 0; j < i; ++j) {
                    d = dis(cir[j], cir[i]);
                    mp[i][j] = mp[j][i] = d;
                }
            }
            ans = prim();
            printf("%.3lf\n", ans);
        }
        return 0;
    }


    POJ 1039

    黑书上的练习题,为了这道题我啥都没干,看了一天多的黑书。。。=_=!   以下内容摘自黑书:

       题意:有一宽度为1的折线管道,上面顶点为(xi,yi),所对应的下面顶点为(xi,yi-1),假设管道都是不透明的,不反射的,光线从左边入口处的(x0,y0),(x,y0-1)之间射入,向四面八方传播,求解光线最远能传播到哪里(取x坐标)或者是否能穿透整个管道.
         如果一根光线自始至终都未擦到任何顶点,那么它肯定不是最优的,因为可以通过平移来使之优化,如果只碰到一个顶点,那也不是最优的,可以通过旋转,使它碰到另一个顶点,并且更优,即最优光线一定擦到一个上顶点和一个下顶点.
         所以可以任取一个上顶点和一个下顶点,形成直线L,若L能射入左入口,即当x=x0时,直线L在(x0,y0)和(x0,y0-1)之间,则是一条可行光线.再从左到右一次判断每条上,下管道是否与L相交,相交则求交点,并把交点x值与当前最佳值比较,若所有管壁都不予L相交,则说明L射穿了整个管道.

    然后参考庄神的模板做的,Orz。。。

    渣代码:

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int MAXN = 100;
    const double eps = 1e-8;
    const double inf = ~0u;
    
    struct point {
        double x;
        double y;
    };
    
    point up[MAXN], dn[MAXN];
    int n;
    
    int dbcmp(double x) {
        if(x > eps)    return 1;
        else if(x < -eps)    return -1;
        return 0;
    }
    
    double det(double x1, double y1, double x2, double y2) {
        return x1*y2 - x2*y1;
    }
    
    double cross(point a, point b, point c) {
        return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
    }
    
    int segcross(point a, point b, point c, point d) {    //判线段与直线相交,做一次跨立实验即可
        int d1, d2;
        d1 = dbcmp(cross(a, b, c));
        d2 = dbcmp(cross(a, b, d));
        if(d1*d2 <= 0)    return true;
        return false;
    }
    
    double get_x(point a, point b, point c, point d) {    //从题意可以知道,不存在与y轴平行的直线,所以可以直接用点斜式求交点
        double k1, k2, c1, c2;
        k1 = (a.y - b.y)/(a.x - b.x);
        k2 = (c.y - d.y)/(c.x - d.x);
        c1 = a.y - k1*a.x;
        c2 = c.y - k2*c.x;
        return (c2 - c1)/(k1 - k2);
    }
    
    double solve(point a, point b) {
        int i = 0;
        double t, ans = -inf;
        while(i < n && segcross(a, b, up[i], dn[i]))    ++i;
    
        if(i == 0)    return -inf;
        if(i == n)    return up[n-1].x;
    
        if(segcross(a, b, up[i], up[i-1])) {
            t = get_x(a, b, up[i], up[i-1]);
            if(dbcmp(t - ans) > 0)    ans = t;
        }
    
        if(segcross(a, b, dn[i], dn[i-1])) {
            t = get_x(a, b, dn[i], dn[i-1]);
            if(dbcmp(t - ans) > 0)    ans = t;
        }
        return ans;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i, j;
        bool flag;
        double res;
        while(scanf("%d", &n), n) {
            for(i = 0; i < n; ++i) {
                scanf("%lf%lf\n", &up[i].x, &up[i].y);
                dn[i].x = up[i].x; dn[i].y = up[i].y - 1;
            }
            res = -inf; flag = true;
            for(i = 0; i < n && flag; ++i) {
                for(j = i + 1; j < n && flag; ++j) {
                    res = max(res, solve(up[i], dn[j]));
                    res = max(res, solve(dn[i], up[j]));
                    if(dbcmp(res - up[n-1].x) >= 0)    flag = false;
                }
            }
            if(flag)    printf("%.2f\n", res);
            else    puts("Through all the pipe.");
        }
        return 0;
    }

     POJ 1408

      纯属YY题,叉积求交点,叉积求多边形面积。然后取最大的面积就ok了。昨天调了一晚上,还以为是叉积求交点出错了呢,原来是求面积的时候算错三角形了,唉。。。T_T

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int M = 50;
    const double eps = 1e-8;
    const double inf = ~0u;
    
    struct point {
        double x;
        double y;
        point(double a = 0, double b = 0) : x(a), y(b) {}
    };
    
    point a[M], b[M], c[M], d[M], p[M][M];
    
    int dbcmp(double x) {
        if(x > eps)    return 1;
        else if(x < -eps)    return -1;
        return 0;
    }
    
    double det(double x1, double y1, double x2, double y2) {
        return x1*y2 - x2*y1;
    }
    
    double cross(point a, point b, point c) {
        return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
    }
    
    point seg(point a, point b, point c, point d) {    //叉积求交点
        double s1, s2;
        s1 = cross(a, b, c);
        s2 = cross(a, b, d);
        point t;
        t.x = (c.x*s2 - d.x*s1)/(s2 - s1);
        t.y = (c.y*s2 - d.y*s1)/(s2 - s1);
        return t;
    }
    
    double area(point a, point b, point c, point d) {    //叉积求面积
        double ret = 0;
        ret = cross(a, b, c) + cross(a, c, d);
        return fabs(ret)/2.0;
    }
    
    void init(int n) {
        int i;
        for(i = 0; i < n; ++i) {
            scanf("%lf", &a[i].x);
            a[i].y = 0;
        }
        for(i = 0; i < n; ++i) {
            scanf("%lf", &b[i].x);
            b[i].y = 1;
        }
        for(i = 0; i < n; ++i) {
            scanf("%lf", &c[i].y);
            c[i].x = 0;
        }
        for(i = 0; i < n; ++i) {
            scanf("%lf", &d[i].y);
            d[i].x = 1;
        }
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int n, i, j;
        double res = -inf, ta;
        point p1, p2, p3, p4;
        while(scanf("%d", &n), n) {
            init(n);
            res = -inf;
    
            p[0][0] = point(0, 0); p[n+1][n+1] = point(1, 1);
            p[0][n+1] = point(0, 1); p[n+1][0] = point(1, 0);
    
            for(i = 0; i < n; ++i)     p[0][i+1] = c[i];
            for(i = 0; i < n; ++i)    p[n+1][i+1] = d[i];
            for(i = 0; i < n; ++i)    p[i+1][0] = a[i];
            for(i = 0; i < n; ++i)    p[i+1][n+1] = b[i];
    
            for(i = 0; i < n; ++i) {
                for(j = 0; j < n; ++j) {
                    p[i+1][j+1] = seg(a[i], b[i], c[j], d[j]);
                }
            }
            ++n;
            for(i = 0; i <= n; ++i) {
                for(j = 0; j <= n; ++j) {
                    if(i + 1 <= n && j + 1 <= n) {
                        ta = area(p[i][j], p[i+1][j], p[i+1][j+1], p[i][j+1]);
                        if(dbcmp(ta - res) > 0)        res = ta;
                    }
                }
            }
            printf("%.6lf\n", res);
        }
        return 0;
    }

     POJ 1584

    题意:给出多边形顶点的个数,圆的半径,圆的x,y坐标,并且按顺时针(或逆时针)给出一个序列,表示一个多边形,判断这个多边形是不是凸包。如果是,判断圆是否在多边形内。。。

    1、判凸包可以枚举没三个相邻的点a, b, c求叉积Pab×Pac。每一组Pab×Pac 都同号则说明多多边形是叉积。

    2、判圆心是否在多边形每部。枚举相邻的两个点a,b,设圆心左边为c,然后同1,求叉积,判符号。

    3、求圆心到多边形每条边的距离,与半径比较,如果小于半径则PEG WILL NOT FIT。设一条边的两个端点为a, b,圆心为c。这里可以用叉积求三角形Sabc的面积和ab的长度L。c到ab的距离就是Sabc/L。

    ps:第一次提交的时候条件1判错了,wa了一次。T_T

    View Code
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    
    using namespace std;
    
    const int N = 1100;
    const double eps = 1e-8;
    
    struct point {
        double x;
        double y;
    }p[N], cir;
    
    double rad;
    int n;
    
    int dbcmp(double x) {
        if(x > eps)    return 1;
        else if(x < -eps)    return -1;
        return 0;
    }
    
    double det(double x1, double y1, double x2, double y2) {
        return x1*y2 - x2*y1;
    }
    
    double cross(point a, point b, point c) {   //叉积
        return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
    }
    
    double dis(point a, point b, point c) {   //求点到边的距离
        double s, l;
        s = fabs(cross(c, a, b));   //Sabc * 2
        l = sqrt((b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y));   //L
        return s/l;
    }
    
    void solve() {
        double flag, t, d;
        int i;
        flag = cross(p[0], p[1], p[2]);
        for(i = 1; i < n-1; ++i) {    //判是否为凸包
            t = cross(p[i], p[i+1], p[i+2]);
            if(dbcmp(flag*t) < 0) {
                puts("HOLE IS ILL-FORMED");    
                return ;
            }
        }
        flag = cross(p[0], p[1], cir);
        d = dis(p[0], p[1], cir);
    
        if(dbcmp(rad - d) > 0)    {puts("PEG WILL NOT FIT"); return ;}
        for(i = 1; i < n; ++i) {    //判圆是否在多边形内并且圆心到直线的距离 <= radiu
    
            d = dis(p[i], p[i+1], cir);
            t = cross(p[i], p[i+1], cir);
    
            if(dbcmp(flag*t) < 0) {
                puts("PEG WILL NOT FIT");
                return ;
            }
            if(dbcmp(rad - d) > 0)    {puts("PEG WILL NOT FIT"); return;}
        }
        puts("PEG WILL FIT");
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i;
        while(~scanf("%d", &n)) {
            if(n < 3)    break;
            scanf("%lf%lf%lf", &rad, &cir.x, &cir.y);
            for(i = 0; i < n; ++i) {
                scanf("%lf%lf", &p[i].x, &p[i].y);
            }
            p[n] = p[0];
            solve();
        }
        return 0;
    }

    POJ 2187

    题意很清楚,求凸包的直径。表示不会旋转卡壳,只能暴力的来做。黑书上的Graham-Scan算法。O(n)的时间复杂度求凸包,然后对极点两两枚举,求最大距离。感觉这样写bug很明显,50000的数据如果都在凸包上,那。。。必定TLE!-_-! 丫的,第一次写的时候完了排序了。TLE*2 T_T!

    渣代码:375+ms

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 50007;
    const double eps = 1e-6;
    const double inf = ~0u;
    
    struct point {
        double x;
        double y;
    } p[N];
    
    int st[N], f[N], n, t;
    bool vis[N];
    
    int dbcmp(double x) {
        if(x > eps)    return 1;
        else if(x < -eps)    return -1;
        return 0;
    }
    
    bool cmp(point a, point b) {
        if((dbcmp(a.y - b.y) == 0))    return dbcmp(a.x - b.x) < 0;
        return dbcmp(a.y - b.y) < 0;
    }
    
    double det(double x1, double y1, double x2, double y2) {
        return x1*y2 - x2*y1;
    }
    
    double cross(point a, point b, point c) {
        return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
    }
    
    double dis(point a, point b) {
        return (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y);
    }
    
    void graph(int dir) {   //Graham求凸包
        int i;
        t = 0;
        for(i = 0 ; i < n; ++i) {
            if(vis[i])    continue;
            while(t > 1 && cross(p[st[t-1]], p[st[t]], p[i])*dir < 0)    --t;
            st[++t] = i;
        }
        for(i = 2; i < t; ++i)     vis[st[i]] = true;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i, m, j;
        double res, tmp;
        scanf("%d", &n);
            for(i = 0; i < n; ++i) {
                scanf("%lf%lf", &p[i].x, &p[i].y);
            }
            sort(p, p + n, cmp);
            memset(vis, false, sizeof(vis));
    
            graph(1);
            for(i = 1; i <= t; ++i)    f[i] = st[i]; m = t;
            graph(-1);
            for(i = 1; i < t; ++i) f[m+i] = st[t-i]; m += (t - 1);
    
            res = -inf;
            for(i = 1; i < m - 1; ++i) {
                for(j = i + 1; j < m; ++j) {
                    tmp = dis(p[f[i]], p[f[j]]);
                    if(tmp > res)    res = tmp;
                }
            }
            printf("%.0f\n", res);
        return 0;
    }

     POJ 1113

    题意是给一组点的坐标表示的多边形,要从外围用绳子围起来,并且绳子到多边形的最短距离为L。求绳子的最短长度。

    很明显,找出凸包,然后求各相邻极点间的距离,最后加上半径为L的圆的周长。

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1024;
    const double eps = 1e-6;
    const double pi = acos(-1.0);
    
    struct point {
        double x;
        double y;
    } p[N];
    
    int st[N], t, n;
    bool vis[N];
    int f[N];
    
    int dbcmp(double x) {
        if(x > eps)    return 1;
        else if(x < -eps)    return -1;
        return 0;
    }
    
    bool cmp(point a, point b) {
        if(dbcmp(a.y - b.y) == 0)    return dbcmp(a.x - b.x) < 0;
        return dbcmp(a.y - b.y) < 0;
    }
    
    double det(double x1, double y1, double x2, double y2) {
        return x1*y2 - x2*y1;
    }
    
    double cross(point a, point b, point c) {
        return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
    }
    
    double dis(point a, point b) {
        return sqrt((b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y));
    }
    
    void graham(int dir) {
        int i; t = 0;
        for(i = 0; i < n; ++i) {
            if(vis[i])    continue;
            while(t > 1 && dir*cross(p[st[t-1]], p[st[t]], p[i]) < 0)    --t;
            st[++t] = i;
        }
        for(i = 2; i < t; ++i)    vis[st[i]] = true;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i, m;
        double l, res;
        while(~scanf("%d%lf", &n, &l)) {
            for(i = 0; i < n; ++i) {
                scanf("%lf%lf", &p[i].x, &p[i].y);
            }
            sort(p, p + n, cmp);
            memset(vis, 0, sizeof(vis));
            graham(1);
            for(i = 1; i <= t; ++i)    f[i] = st[i]; m = t;
            graham(-1);
            for(i = 1; i < t; ++i)    f[m + i] = st[t - i]; m += t-1;
            res = 0;
            for(i = 1; i < m; ++i) {
                res += dis(p[f[i]], p[f[i + 1]]);
            }
            res += 2*pi*l;
            printf("%.0lf\n", res);
        }
        return 0;
    }

     

     ps:计算几何这几道水题终于刷完了,有的地方确实很恶心人。。。比如说POJ 1408,YY题,因为不熟,我敲了一个晚自习。。。发现计算几何的模板好多,记记模板,加油!^_^

  • 相关阅读:
    C语言——总结回顾
    C语言——第十四、十五周作业
    题目思路——统计素数并求和
    题目思路——单词长度
    C语言——第七周作业
    C语言——第六周作业
    C语言——第四次作业
    C语言——第三次作业
    C语言——第二次作业
    C语言——第一次作业
  • 原文地址:https://www.cnblogs.com/vongang/p/2435695.html
Copyright © 2020-2023  润新知