现在的位置: 首页 > 综合 > 正文

POJ 计算几何

2012年03月18日 ⁄ 综合 ⁄ 共 9038字 ⁄ 字号 评论关闭

计算几何学

几何公式

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

【上篇】
【下篇】

抱歉!评论已关闭.