• poj3241(曼哈顿最小生成树)


    题目链接:http://poj.org/problem?id=3241

    题意:求曼哈顿距离最小生成树第k大的边。曼哈顿距离:点A(x1,y1)与点B(x2,y2)距离d=abs(x1-x2)+abs(y1-y2)。

    求解曼哈顿距离最小生成树的方法简述:

      如果直接两两点建边,总边数则为O(n2)条,用时间复杂度O(N+E)的Prim算法,时间复杂度O(E*logE)的kruskal算法都是会超时的(这里N为点数,E为边数)

      而这里的解题思路就是将边数复杂度降到O(n),因为大量的边是没有用到的,只要每45度方向距离该点最近的一个点连边即可。

      

      证明就不证了,直接上结论,假如B,C都是A 在y轴向右45度区域内并且 |AB|<=|AC|,可证得|BC|<=|AC|,所以|AB|+|BC|<=|AC|+|AB|或|AC|+|BC|,即A,B,C三点距离最短为 每45度方向距离最短之和(这里距离都是曼哈顿距离) 证明博客链接:https://blog.csdn.net/huzecong/article/details/8576908

       依据上面可得只要连接每个点与八个方向距离最小的点连边即可。

       又因为,边是双向的,我们只要连R1,R2,R3,R4四个方向即可。比如:(上图)B是A R1最小距离点,A是B R5最小距离点,我们只要连R1,R5不连,即A连向B即可知道AB的关系。

      思路出来了,那么怎么处理呢?我们先考虑一个方向,例如R1,(其他三个方向类比即可)。

       在某个点A(x0,y0)的这个区域内的点B(x1,y1)满足x1xy1x1>y0x0。那么B就在A的R1上

      在A的R1区域内距离A最近的点也即满足条件的点中x+y最小的点。因此我们可以将所有点按x坐标排序,再按yx离散。

      用线段树或者树状数组维护大于当前点的yx的最小的x+y对应的点(也就是维护区间最小值)这里用的是树状数组,因为代码较简单。

    树状数组知识:https://www.cnblogs.com/hsd-/p/6139376.html

       这样R1区域就处理完了,而其他三个区域的点只要用数学知识 翻转和对称 转化成R1处理即可。比如,R2区域将关于y=x对称(即swap(x,y)),再将关于

    x轴对称(即x=-x)可求R3,再关于y=x对称求R4。

    还有些细节可以看代码:

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=1e5;
    struct point{
        int x,y,id;
        bool operator < (const point &a)const{
            return a.x==x?y<a.y:x<a.x;
        }
    }p[maxn];//按x从小到大排序,再按y-x离散 
    
    struct node{
        int u,v,w;
        bool operator < (const node &a)const{
            return w<a.w;
        }
    }edge[maxn<<3];
    
    struct Bit{//树状数组维护区间最小值 
        int pos,w;
        void init(){ pos=-1;w=inf;}
    }bit[maxn];
    
    int fa[maxn],a[maxn],b[maxn];//数组a,b用于离散化,求得 在y轴向右45度的所有点 
    int n,m,k,cnt;
    
    int find(int x){
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    void add(int u,int v,int w){
        edge[++cnt].u=u;edge[cnt].v=v;edge[cnt].w=w;
    }
    int lowbit(int x){return x&(-x);}
    int dist(point a,point b){return abs(a.x-b.x)+abs(a.y-b.y);}
    int query(int x,int m){//查询区间p[i].x+p[i].y最小值 
        int minx=inf,pos=-1;
        for(int i=x;i<=m;i+=lowbit(i)){
            if(minx>bit[i].w){
                minx=bit[i].w;
                pos=bit[i].pos;
            }
        }
        return pos;
    }
    void update(int x,int y,int pos){//维护区间p[i].x+p[i].y最小值 
        for(int i=x;i>=1;i-=lowbit(i)){
            if(y<bit[i].w){
                bit[i].w=y;
                bit[i].pos=pos;
            }
        }
    }
    void solve(){//Kruskal求第k大的边 
        int tot=0;
        sort(edge+1,edge+cnt+1);
        for(int i=1;i<=n;i++)
            fa[i]=i;
        for(int i=1;i<=cnt;i++){
            if(find(edge[i].u)!=find(edge[i].v)){
                tot++;
                fa[find(edge[i].u)]=find(edge[i].v);
            }
            if(tot==k){
                printf("%d
    ",edge[i].w);
                break;
            }
        }
    }
    void caledge(){
        sort(p+1,p+n+1);
        for(int i=1;i<=n;i++)
            a[i]=b[i]=p[i].y-p[i].x;
        sort(b+1,b+n+1);
        m=unique(b+1,b+n+1)-b;//去重 
        for(int i=1;i<=m;i++)//初始化 
            bit[i].init();
        for(int i=n;i>=1;i--){
        //只能从x大的 开始遍历 ,图像从右往左遍历,才能查询和维护R1,R2,R3,R4区域最小距离点 
            int x=lower_bound(b+1,b+m+1,a[i])-b+1;//从y轴向右45度的区域寻找,即b.y-b.x>a.y-a.x 
            int pos=query(x,m);//查找与点p[i]最近的点(y轴向右45度的区域) 
            if(pos!=-1)//查询到就连边 
                add(p[i].id,p[pos].id,dist(p[i],p[pos]));
            update(x,p[i].x+p[i].y,i);//维护区间最小值 
        }
    }
    int main(){
        scanf("%d%d",&n,&k);
        k=n-k;cnt=0;
        for(int i=1;i<=n;i++)    
            scanf("%d%d",&p[i].x,&p[i].y),p[i].id=i;
        for(int j=0;j<4;j++){
            if(j==1||j==3)
                for(int i=1;i<=n;i++)
                    swap(p[i].x,p[i].y);
            else if(j==2)
                for(int i=1;i<=n;i++)
                    p[i].x=-p[i].x;
            caledge();
        }
        solve();
        return 0;
    }
  • 相关阅读:
    vue项目本地调试,内网穿透
    EMQ开启mysql认证
    vsftpd配置安装
    express使用https
    vue实现图片的上传和删除
    Linux下获取java堆栈文件并进行分析
    kill -3 PID命令获取java应用堆栈信息
    Linux下的java虚拟机性能监控与故障处理命令
    k8s下的eureak服务注册失败(cannot execute request on any known server)解决
    MariaDB主从复制虚拟机实战
  • 原文地址:https://www.cnblogs.com/xiongtao/p/10533314.html
Copyright © 2020-2023  润新知