计算几何 | 二维几何基础入门题

求多边形重心HDU-1115 Lifting the Stone

解题思路

1.设三角形三个顶点 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3,重心坐标为 x 1 + x 2 + x 3 3 \frac{x_1+x_2+x_3}{3} 3x1+x2+x3

2.求多边形的重心可以对每个三角形的有向面积加权平均,那有向面积裆燃是用叉积求喽。

注意事项

1.不用每一步都做除法。最后除 6 6 6 即可。

6 = 2 × 3 6 = 2 × 3 6=2×3 解释:每一步计算三角形的有向面积的时候要除以 2 2 2,计算三角形重心坐标要除以 3 3 3

2.有向面积有正负,不能取绝对值。

3.题目是说要四舍五入保留两位小数,但是我加 0.005 0.005 0.005 之后不太对劲,咋回事?

我的妈呀原来 printf("%.2f", x)根据要保留的位数自动进行四舍五入的操作…

我直接大吃一惊我仿佛是个假人

赶紧学一下:

%.2f自动四舍五入

如上,如果不想让其四舍五入:

#include <stdio.h>
#include <math.h>
int main()
{
    
    
    double d = 1.199;
    printf("%.2f", floor(d * 100) / 100);
    return 0;
}

参考代码

#include<bits/stdc++.h>
using namespace std;
#define LOCAL  //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-6;  //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1000010;

int readint(){
    
    
    int x; scanf("%d", &x); return x;
}
//计算几何
int sgn(db x) {
    
      //判断浮点数是否为0,为0时返回0
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

struct point{
    
    
    db x, y;
    point(db X = 0, db Y = 0) {
    
    x = X, y = Y;}
    point operator + (point B) {
    
    return point{
    
    x + B.x, y + B.y};}
    point operator - (point B) {
    
    return point{
    
    x - B.x, y - B.y};}
    point operator / (db k) {
    
    return point{
    
    x / k, y / k};}
    point operator * (db k) {
    
    return point{
    
    x * k, y * k};}

    bool operator == (point B) {
    
    return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
}p[maxn];

db cross(point A, point B) {
    
    return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
    
    return hypot(A.x - B.x, A.y - B.y);}
db polygon_area(point *p, int n) {
    
      //求多边形的面积
    db area = 0;
    _for(i, 0, n) {
    
    
        area += cross(p[i], p[(i + 1)%n]);
    }
    return area/2;  //是按照三角形来算,所以这里统一除2
}

point polygon_center(point *p, int n) {
    
    
    point ans(0, 0);
    db area = polygon_area(p, n);
    if (area == 0) return ans;
    _for(i, 0, n) {
    
    
        ans = ans + (p[i] + p[(i + 1)%n]) * cross(p[i], p[(i + 1)%n]);
    }
    return ans/area/6;
}

int main() {
    
    
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int t, n;
    scanf("%d", &t);
    while (t--) {
    
    
        scanf("%d", &n);
        _for(i, 0, n) {
    
    
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        point ans = polygon_center(p, n);
        printf("%.2f %.2f\n", ans.x, ans.y);  //不用加0.005
    }
    return 0;
}

判断线段相交+并查集HDU-1558 Segment set

解题思路

读入线段,判断线段是否于已有的线段相交,用并查集维护某个线段所在集合的总个数。

注意事项

1.注意当前线段对应的根节点 r 1 r1 r1 是会更新的,所以每次要重新查询,一开始的时候写在循环外面了,导致错误。

2.输出个数:There is a blank line between test cases.

注意输出不同case之间空一行。

3.注意并查集的巧妙写法。

对集合 s s s 的根节点来说, s [ r o o t ] = − n s[root] = -n s[root]=n ,其绝对值 n n n 为集合元素个数。

其余节点 o t h e r other other s [ o t h e r ] = r o o t s[other] = root s[other]=root ,对应的值根节点的序号。

故可以根据 s [ i ] s[i] s[i] 的值是否是负数来判断是否为集合的根节点。

这一性质运用于集合的合并当中,显得很优雅:

void union_set(int r1, int r2) {
    
      //合并根节点为r1,r2的两个集合
    if (s[r1] > s[r2]) {
    
      //注意二者均为负数,说明r1的元素少
        s[r2] += s[r1];  //一定先更新元素的个数
        s[r1] = r2;  //再更新对应的根节点
    } else {
    
    
        s[r1] += s[r2];
        s[r2] = r1;
    }
}

参考代码

#include<bits/stdc++.h>
using namespace std;
#define LOCAL  //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-6;  //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1020;

int readint(){
    
    
    int x; scanf("%d", &x); return x;
}

//并查集操作
int s[maxn];  //每个集合的根节点为负数,其绝对值为元素个数,其余节点对应根节点的序号
void init_set() {
    
    
    fill(s, s + maxn, -1);
}
//路径压缩的递推写法
//防止爆栈
int find_set(int x) {
    
    
    int r = x;  //找根节点
    while (s[r] > 0) r = s[r];
    int i = x, j;  //路径压缩
    while (i != r) {
    
    
        j = s[i];
        s[i] = r;
        i = j;
    }
    return r;
}

void union_set(int r1, int r2) {
    
      //合并
    if (s[r1] > s[r2]) {
    
      //r1的元素少
        s[r2] += s[r1];  //一定先更新
        s[r1] = r2;
    } else {
    
    
        s[r1] += s[r2];
        s[r2] = r1;
    }
}

//计算几何
int sgn(db x) {
    
      //判断浮点数是否为0,为0时返回0
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

struct point{
    
    
    db x, y;
    point(db X = 0, db Y = 0) {
    
    x = X, y = Y;}
    point operator + (point B) {
    
    return point{
    
    x + B.x, y + B.y};}
    point operator - (point B) {
    
    return point{
    
    x - B.x, y - B.y};}
    point operator / (db k) {
    
    return point{
    
    x / k, y / k};}
    point operator * (db k) {
    
    return point{
    
    x * k, y * k};}
    bool operator == (point B) {
    
    return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
};

struct segment{
    
    
    point a, b;
}seg[maxn];

db cross(point A, point B) {
    
    return A.x * B.y - A.y * B.x;}//求×积

db distance(point A, point B) {
    
    return hypot(A.x - B.x, A.y - B.y);}

//判断两线段相交
bool cross_segment(point a, point b, point c, point d) {
    
    
    db c1 = cross(b - a, c - a), c2 = cross(b - a, d - a);
    db d1 = cross(d - c, a - c), d2 = cross(d - c, b - c);
    return sgn(c1 * c2) <= 0 && sgn(d1 * d2) <= 0;
}

int main() {
    
    
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int t, n;
    char ch[10];
    db x1, y1, x2, y2;
    scanf("%d", &t);
    _for(i, 0, t) {
    
    
        if (i) printf("\n");
        init_set();
        scanf("%d", &n);
        int cnt = 1;
        _for(i, 0, n) {
    
    
            scanf("%s", ch);
            if (ch[0] == 'P') {
    
    
                scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
                seg[cnt] = segment{
    
    point(x1, y1), point(x2, y2)};
                _rep(j, 1, cnt - 1) {
    
    
                    if (cross_segment(seg[j].a, seg[j].b, seg[cnt].a, seg[cnt].b)) {
    
    
                        int r1 = find_set(cnt);
                        int r2 = find_set(j);
                        if (r1 != r2) {
    
    
                            union_set(r1, r2);
                        }
                    }
                }
                cnt++;
            } else {
    
    
                int pos = readint();
                printf("%d\n", abs(s[find_set(pos)]));
            }
        }
    }
    return 0;
}

参考资料

算法竞赛从入门到进阶

猜你喜欢

转载自blog.csdn.net/Encore47/article/details/109664944
今日推荐