UOJ Logo zhouzixuan的博客

博客

bzoj 2458: [BeiJing2011]最小三角形

2015-09-15 13:01:23 By zhouzixuan
【题目描述】:
    平面上有N个点,Xaviera想找出周长最小的三角形。
    由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。
    为了减小问题的难度,这里的三角形也包括共线的三点。
【题目解法】:
    我们可以用类似分治法求最近点对的方法去做
    对点集按照x坐标排序
    首先设计solve(l,r)表示求[l,r]间的点构成的最小周长三角形
    然后我们分治solve(l,mid) solve(mid+1,r)
    现在我们就要考虑处理两边的点共同构成的最小周长三角形
    我们知道在该题中三角形内任意两边之和大于等于第三边
    所以假设我们现在的最有答案为ans,那么任意一条边的长度不得超过ans/2才可能成为最优解
    所以我们以a[mid].x作为中间的基准,向两边各延伸ans/2个单位,只有这些点才可能构成最优解
    然后我们把这些点按照y坐标排序
    如果两个点的y坐标之差>ans/2那么显然也不能成为最优解
    然后据说这样每个点最多和16个点进行判断
    所以复杂度就是O(n log n log n * 16 * 16)
    当然内部排序可以归并做到O(n log n * 16 * 16)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define sqr(x) ((x)*(x))
using namespace std;
const int N=200005;
const double INF=320832083208.8;
struct point {double x,y;} a[N],b[N];
int n; double ans;
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10LL+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline double dist(const point &a,const point &b)
{
    return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline bool cmpx(const point &a,const point &b)
{
    return a.x<b.x;
}
inline bool cmpy(const point &a,const point &b)
{
    return a.y<b.y;
}
inline double tangle(const point &a,const point &b,const point &c)
{
    return dist(a,b)+dist(a,c)+dist(b,c);
}
void init()
{
    n=getint(); ans=INF;
    for (int i=1; i<=n; i++) a[i].x=getint(),a[i].y=getint();
    sort(a+1,a+n+1,cmpx);
}
void solve(int l,int r)
{
    if (l+1>=r) return;
    if (l+2==r) {ans=min(ans,tangle(a[l],a[l+1],a[r])); return;}
    int mid=(l+r)/2; solve(l,mid); solve(mid+1,r);
    double nowans=ans/2.0; int tot=0;
    for (int i=l; i<=r; i++) if (fabs(a[i].x-a[mid].x)<=nowans) b[++tot]=a[i];
    sort(b+1,b+tot+1,cmpy);
    for (int i=1; i<=tot; i++)
        for (int j=i+1; j<=tot; j++)
        {
            if (b[j].y-b[i].y>nowans) break;
            for (int k=j+1; k<=tot; k++)
            {
                if (b[k].y-b[i].y>nowans) break;
                ans=min(ans,tangle(b[i],b[j],b[k]));
            }
        }
}
int main()
{
    //freopen("1.in","r",stdin);
    init();
    solve(1,n);
    printf("%.6lf\n",ans);
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。