zoukankan      html  css  js  c++  java
  • 二维曼哈顿最小生成树



    所以我们只要求一个点在其45°角的区域内离他最近的点就行了,而这可以用线段树或树状数组解决

    我们以y轴正半轴往右偏45°角的区域为例:

    点j在点i的这个区域要满足的条件是:

    yj-xj>yi-xi

    且xj>xi

    那么我们将点以x为第一关键字,y为第二关键字,排序后倒序插入线段树

    线段树的线段这一维是离散后的y-x,值是y+x

    我们要求的是大于yi-xi的最小的y+x,而xj>xi这个条件已经由插入顺序满足了

    这样我们成功的解决了这个区域的点

    而其他区域的点我们可以通过坐标变换转移到这个区域

    由于对称性,我们注意到其实只要求x轴或y轴正半轴所在的四个区域就行了

    那么这个问题就这样解决了


    #include <map>
    #include <set>
    #include <cmath>
    #include <ctime>
    #include <stack>
    #include <queue>
    #include <cstdio>
    #include <memory>
    #include <cctype>
    #include <bitset>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstring>
    #include <iostream>
    #include <iomanip>
    #include <algorithm>
    #include <functional>
    
    //#define FIN freopen("input.txt","r",stdin);
    //#define FOUT freopen("output.txt","w+",stdout);
    using namespace std;
    typedef long long ll;
    
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    const double eps=1e-8;
    const double Pi=acos(-1.0);
    const int N=50010;
    
    struct point
    {
        int x,y,id;
        bool operator<(const point p)const
        {
            return x!=p.x?x<p.x:y<p.y;
        }
    } p[N];
    struct BIT
    {
        int min_val,pos;
        void init()
        {
            min_val=INF;
            pos=-1;
        }
    } bit[N];
    int par[N];//并查集中父亲
    int hight[N];//并查集树的高度
    struct edge
    {
        int u,v,cost;
    };
    edge G[N<<2];//边集(边数)
    int V,E;//顶点数和边数
    int get_Manhadm_dis(point a,point b)
    {
        return abs(a.x-b.x)+abs(a.y-b.y);
    }
    void addedge(int u,int v,int w)
    {
        G[E].u=u;
        G[E].v=v;
        G[E++].cost=w;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int val,int pos)
    {
        for(int i=x; i>=1; i-=lowbit(i))
            if(val<bit[i].min_val)
            {
                bit[i].min_val=val;
                bit[i].pos=pos;
            }
    }
    int ask(int x,int m)
    {
        int min_val=INF;
        int pos=-1;
        for(int i=x; i<=m; i+=lowbit(i))
            if(bit[i].min_val<min_val)
            {
                min_val=bit[i].min_val;
                pos=bit[i].pos;
            }
        return pos;
    }
    void make_edge()
    {
        int a[N],b[N];
        for(int dir=0; dir<4; dir++)
        {
            if(dir==1||dir==3)
                for(int i=0; i<V; i++)
                    swap(p[i].x,p[i].y);
            else if(dir==2)
                for(int i=0; i<V; i++)
                    p[i].x=-p[i].x;
            sort(p,p+V);
            for(int i=0; i<V; i++)
                a[i]=b[i]=p[i].y-p[i].x;
            sort(b,b+V);
            int m=unique(b,b+V)-b;
            for(int i=1; i<=m; i++)
                bit[i].init();
            for(int i=V-1;i>=0; i--)
            {
                int pos=lower_bound(b,b+m,a[i])-b+1;
                int ans=ask(pos,m);
                if(ans!=-1)
                    addedge(p[i].id,p[ans].id,get_Manhadm_dis(p[i],p[ans]));
                update(pos,p[i].x+p[i].y,i);
            }
        }
    }
    //并查集初始化
    void Init_union_find(int n)
    {
        for(int i=0; i<n; i++)
        {
            par[i]=i;
            hight[i]=0;
        }
    }
    //查询树的根
    int find(int x)
    {
        if(par[x]==x)
            return x;
        else
            return par[x]=find(par[x]);
    }
    //合并x和y所属的集合
    void unite(int x,int y)
    {
        x=find(x);
        y=find(y);
        if(x==y)
            return ;
        if(hight[x]<hight[y])
            par[x]=y;
        else
        {
            par[y]=x;
            if(hight[x]==hight[y])
                hight[x]++;
        }
    }
    //判断x和y是否属于同一个集合
    bool same(int x,int y)
    {
        return find(x)==find(y);
    }
    bool cmp(const edge& a,const edge& b)
    {
        return a.cost<b.cost;
    }
    int kruskal()
    {
        sort(G,G+E,cmp);//按照edge.cost的顺序从小到大排列
        Init_union_find(V);//并查集初始化
        int ans=0;
        for(int i=0; i<E; i++)
        {
            edge e=G[i];
            if(!same(e.u,e.v))
            {
                unite(e.u,e.v);
                ans+=e.cost;
            }
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&V);
        for(int i=0; i<V; i++)
        {
            scanf("%d %d",&p[i].x,&p[i].y);
            p[i].id=i+1;
        }
        E=0;
        make_edge();
        printf("%d
    ",kruskal());
    }



  • 相关阅读:
    《Centos服务器版安装教程》
    从CentOS官网下载系统镜像详细教程
    一键LNMP文件
    Centos 7 ip地址
    cmd常用命令
    bat命令
    JAVA学习资源整理
    DevOps 高效 shell 命令
    编程范式:命令式编程(Imperative)、声明式编程(Declarative)和函数式编程(Functional)
    Java 中的函数式编程(Functional Programming):Lambda 初识
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9387094.html
Copyright © 2011-2022 走看看