BZOJ 2458 最小三角形 | 平面分治

BZOJ 2458 最小三角形

题面

一个平面上有很多点,求他们中的点组成的周长最小的三角形的周长。

题解

平面最近点对差不多,也是先把区间内的点按x坐标从中间分开,递归处理,然后再处理横跨中线的三角形。

如何缩小范围?设左右两个子区间发现的最小周长是d,则与中线距离超过d / 2都没有用了,对于一个点,所有与它距离超过d / 2的点也都没有用。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 200005;
int n;
struct point {
    double x, y;
    bool operator < (const point &b) const{
    return x < b.x;
    }
    point operator - (const point &b){
    return (point){x - b.x, y - b.y};
    }
    double norm(){
    return sqrt(x * x + y * y);
    }
} p[N], a[N], b[N], c[N];

double calc(point x, point y, point z){
    return (x - y).norm() + (y - z).norm() + (z - x).norm();
}
double solve(int l, int r){
    if(l == r) return 1e20;
    int mid = (l + r) >> 1;
    double xmid = (p[mid].x + p[mid + 1].x) / 2;
    double d = min(solve(l, mid), solve(mid + 1, r));
    int pos = l, pb = 0, pc = 0, pl = l, pr = mid + 1;
    while(pos <= r)
    if(pl <= mid && (pr > r || p[pl].y < p[pr].y)){
        if(p[pl].x > xmid - d / 2) b[++pb] = p[pl];
        a[pos++] = p[pl++];
    }
    else{
        if(p[pr].x < xmid + d / 2) c[++pc] = p[pr];
        a[pos++] = p[pr++];
    }
    for(int i = l; i <= r; i++)
    p[i] = a[i];
    if(l + 1 == r) return 1e20;
    for(int i = 1, j = 1; i <= pb; i++){
    while(j <= pc && b[i].y - c[j].y > d / 2) j++;
    for(int k = j; k <= pc && abs(b[i].y - c[k].y) < d / 2; k++)
        for(int h = k + 1; h <= pc && abs(b[i].y - c[h].y) < d / 2; h++)
        d = min(d, calc(b[i], c[k], c[h]));
    }
    for(int i = 1, j = 1; i <= pc; i++){
    while(j <= pb && c[i].y - b[j].y > d / 2) j++;
    for(int k = j; k <= pb && abs(c[i].y - b[k].y) < d / 2; k++)
        for(int h = k + 1; h <= pb && abs(c[i].y - b[h].y) < d / 2; h++)
        d = min(d, calc(c[i], b[k], b[h]));
    }
    return d;
}
int main(){
    read(n);
    for(int i = 1; i <= n; i++)
    scanf("%lf%lf", &p[i].x, &p[i].y);
    sort(p + 1, p + n + 1);
    printf("%.6lf\n", solve(1, n));
    return 0;
}
时间: 2025-01-09 16:01:45

BZOJ 2458 最小三角形 | 平面分治的相关文章

BZOJ 2458: [BeiJing2011]最小三角形 | 平面分治

题目: 给出若干个点 求三个点构成的周长最小的三角形的周长(我们认为共线的三点也算三角形) 题解: 可以参考平面最近点对的做法 只不过合并的时候改成枚举三个点更新周长最小值,其他的和最近点对大同小异 2#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define N 200010 #define INF 1e20 using namespace std; int

BZOJ 2458 BeiJing2011 最小三角形 计算几何+分治

题目大意:给定平面上的一个点集,求这个点集所能组成的周长最小的三角形 与平面最近点对一个道理- - 这个题也是分治做法 做法如下: 1.记录全局答案ans 2.将所有点按照x值排序 3.定义Solve(l,r)为处理[l,r]区间内的最小三角形 4.对于每层Solve(l,r),将当前区间分成左右两部分,分别递归处理 5.两侧的最小三角形都以处理完毕,现在我们要处理的就是两区间之间的点构成的三角形 6.将本层中与点mid的横坐标之差不超过ans/2的点拎出来,按照纵坐标排序 (其实这步可以直接递

bzoj2458: [BeiJing2011]最小三角形(分治+几何)

题目链接:bzoj2458: [BeiJing2011]最小三角形 学习推荐博客:分治法编程问题之最接近点对问题的算法分析 题解:先将所有点按x值排列,然后每次将当前区间[l,r]分成左右两半递归求解周长最小三角形.考虑到两半区间之间可能有连成最小三角形的情况,设dd为两半区间中最小三角形周长的最小值,筛选满足要求的点(x值与中点坐标x值的距离小于dd),然后按y值排序,进而暴搜出周长最小三角形. 1 #include<cstdio> 2 #include<cmath> 3 #in

【BZOJ2458】【BeiJing2011】最小三角形 计算几何+分治

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42913023 题解:很经典的分治计算几何模型. 我们对点的x坐标排序,然后进行分治,同时分治完了还需要求两边的互相影响. 一.在左边取两个点,右边一个. 二.在右边取两个点,左边一个. 这个时候我们可以对左右两边的点再分别按照y值排序, 当然,因为已经出来了一个比较优的ans,所以当一个点距离两边中界过远,那么我们就把它扔掉再不用管了. 还有就是两边的点,y坐标距离过大的也不能进行选择,

bzoj 2458: [BeiJing2011]最小三角形 题解

[前言]话说好久没有写题解了.到暑假了反而忙.o(╯□╰)o [原题] 2458: [BeiJing2011]最小三角形 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 574  Solved: 177 [Submit][Status] Description Xaviera如今遇到了一个有趣的问题. 平面上有N个点.Xaviera想找出周长最小的三角形. 因为点许多.分布也很乱,所以Xaviera想请你来解决问题. 为了减小问题的难度,这里的三

【模板】【计几】旋转坐标(用于最小三角形以及三角形四边形面积存在性问题)

发现计算几何算法(瞎搞)真的是博大精深. 最大三角形和最大四边形都是旋转卡壳,有模板的.这里的方法还可以求最小三角形还有最小四边形,以及三角形面积存在性问题. 求最小三角形面积(n平方):bzoj3707. 参考:http://www.pianshen.com/article/772191644/ 其实就是先把n方个直线按照斜率先排了序,然后所有点按照距离y轴距离排序(距离有正负),然后枚举的直线相当于y轴,每一次旋转 “ y轴 ” 的时候维护一下这个序列.然后我们发现维护这个序列只需要交换现在

Codeforces 97B Superset 平面分治

题目链接:点击打开链接 题意: 给定一个点集 添加一些点后再把这个点集输出来. 添加完点后使得对于点集内任意2个点都满足下面2条中至少一条 1.在同一水平线上或在同一垂直线上 2.所围成的矩阵里有其他点. 思路: 平面分治 先把点按x轴排序,然后找到中间的点,做一条直线 x = a[mid].x; 然后把所有点都投影到这条直线上,那么对于左边的点就不需要再和右边的进行匹配了. #pragma comment(linker, "/STACK:1024000000,1024000000")

BZOJ 1797 最小割

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1797 题意:给出一个有向图,每条边有流量,给出源点汇点s.t.对于每条边,询问:(1)是否存在一个最小割包含该边?(2)是否所有的最小割都包含该边? 思路:首先求最大流,在残余网络中求强连通 分量.对于每条原图中的边(最大流中添加的反向边不算)<u,v>,该边的残余流量为0且u和v在两个不同的强连通分量中,则存在一个最小割 包含该边:在上述满足且u与s在一个连通分量.v与t在一个连通

BZOJ 1486 最小圈(二分+判负环)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1486 题意:给出一个有向图,边有权值.找到一个环,使得环上边的权值之和除以环上边的个数最小. 思路:二分答案x,每条边权值减去x,之后 找负环.从每个顶点开始DFS,记录到达某个顶点的距离,设当前DFS的顶点为u,距离dis[u].下一个顶点v, 权值w,则dis[u]+w<=0时才DFS(v).另外,若v是已经遍历过的,且dis[u]+w-dis[v](这个dis[v]是之前遍历时