计算几何 | 最近点对入门

模板Quoit Design

解题思路

算最近点对距离的一半即可。

参考代码

#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 1e20
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-8;  //定义浮点数误差
const int MOD = 998244353;
const int maxn = 100010;

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;}
};

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;
}
//判断两线段相交
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;
}

bool cmpxy(point a, point b) {
    
    
    return sgn(a.x - b.x) < 0 || (sgn(a.x - b.x) == 0 && sgn(a.y - b.y) < 0);
}

bool cmpy(point a, point b) {
    
    
    return sgn(a.y - b.y) < 0;  //只对y坐标排序
}

point p[maxn], tmp_p[maxn];

//寻找最近点对
db closest_pair(int left, int right) {
    
    
    db dis = INF;
    if (left == right) return dis;
    if (left + 1 == right) return distance(p[left], p[right]);
    int mid = (left + right) / 2;
    db d1 = closest_pair(left, mid);
    db d2 = closest_pair(mid + 1, right);
    dis = min(d1, d2);
    int k = 0;
    _rep(i, left, right) {
    
    
        if (fabs(p[mid].x - p[i].x) <= dis)  //先剪一部分
            tmp_p[k++] = p[i];
    }
    sort(tmp_p, tmp_p + k, cmpy);
    _for(i, 0, k) {
    
    
        _for(j, i + 1, k) {
    
    
            if (tmp_p[j].y - tmp_p[i].y >= dis) break;
            dis = min(dis, distance(tmp_p[j], tmp_p[i]));
        }
    }
    return dis;
}

int main() {
    
    
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int n;
    while (~scanf("%d", &n) && n) {
    
    
        _for(i, 0, n) {
    
    
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        sort(p, p + n, cmpxy);
        printf("%.2f\n", closest_pair(0, n - 1)/2);
    }
    return 0;
}

删除点+标记Palace

解题思路

设点的个数 n n n, 先计算出一开始的最近点对距离 d d d ,保存他们的位置。

再计算分别去掉这两个点之后的最近点对距离 d 1 , d 2 d_1,d_2 d1,d2

最后的结果即为 d × ( n − 2 ) + d 1 + d 2 d\times(n-2)+d_1+d_2 d×(n2)+d1+d2

注意事项

1.开 l o n g   l o n g long\ long long long

2.分治法求解的时候利用好 o p t opt opt 标记。

参考代码

#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 mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-8;  //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1e5 + 10;
const LL INF = 1e18;
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{
    
    
    LL x, y;
    int id;
    point(LL X = 0, LL Y = 0, int ID = 0) {
    
    x = X, y = Y, id = ID;}
    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 / (LL k) {
    
    return point{
    
    x / k, y / k};}
    point operator * (LL k) {
    
    return point{
    
    x * k, y * k};}
    bool operator == (point B) {
    
    return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
};

LL distance(point A, point B) {
    
    return (A.x-B.x)*(A.x-B.x) + (A.y - B.y)*(A.y - B.y);}

bool cmpxy(point a, point b) {
    
    
    return sgn(a.x - b.x) < 0 || (sgn(a.x - b.x) == 0 && sgn(a.y - b.y) < 0);
}

bool cmpy(point a, point b) {
    
    
    return sgn(a.y - b.y) < 0;  //只对y坐标排序
}
point p[maxn], tmp_p[maxn];

int pos[3];
//寻找最近点对
LL ans = INF;  //开的是全局变量
void closest_pair(int left, int right, int opt) {
    
      //opt为-1时代表是一开始的情况,需要记录pos
    LL tmp;
    if (left == right) return;
    if (left + 1 == right) {
    
    
        if (p[left].id != opt && p[right].id != opt) {
    
    
            tmp = distance(p[left], p[right]);
            if (tmp < ans) {
    
    
                ans = tmp;
                if (opt == -1) {
    
    
                    pos[0] = p[left].id;
                    pos[1] = p[right].id;
                }
            }
        }
        return;
    }
    int mid = (left + right) / 2;
    closest_pair(left, mid, opt);
    closest_pair(mid + 1, right, opt);
    int k = 0;
    _rep(i, left, right) {
    
    
        if (p[i].id != opt && abs(p[mid].x - p[i].x) <= ans)  //先剪一部分
            tmp_p[k++] = p[i];
    }
    sort(tmp_p, tmp_p + k, cmpy);
    _for(i, 0, k) {
    
    
        _for(j, i + 1, k) {
    
    
            if (tmp_p[j].y - tmp_p[i].y >= ans) break;
            tmp = distance(tmp_p[i], tmp_p[j]);
            if (ans > tmp) {
    
    
                ans = tmp;
                if (opt == -1) {
    
    pos[0] = tmp_p[i].id, pos[1] = tmp_p[j].id;}
            }
        }
    }
}

int main() {
    
    
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int t, n;
    LL x, y;
    scanf("%d", &t);
    while (t--) {
    
    
        scanf("%d", &n);
        _for(i, 0, n) {
    
    
            scanf("%lld%lld", &p[i].x, &p[i].y);
            p[i].id = i;
        }
        sort(p, p + n, cmpxy);

        point tmp;
        LL sum = 0;
        ans = INF;
        closest_pair(0, n - 1, -1);
        sum += (n - 2) * ans;

        ans = INF;
        closest_pair(0, n - 1, pos[0]);
        sum += ans;

        ans = INF;
        closest_pair(0, n - 1, pos[1]);
        sum += ans;

        printf("%lld\n", sum);
    }

    return 0;
}

参考资料

算法竞赛从入门到进阶

猜你喜欢

转载自blog.csdn.net/Encore47/article/details/109690933