图论 —— 生成树 —— 曼哈顿距离最小生成树

【概述】

当给出一些二维平面的点时,记两点间距离为曼哈顿距离,此时的最小生成树,称为曼哈顿最小距离生成树。

对于 n 个点来说,最朴素的做法是暴力求出所有所有点两两之间的曼哈顿距离,然后再跑最小生成树算法,以得到曼哈顿最小距离生成树,但这样来做,由于总边数有 n^2 条,时间复杂度会达到 O(n^3) 或 O(n^2 logn)

对于 Kruskal 来说,针对这种曼哈顿距离的 MST 问题,实际上真正用到的点远没有 n^2 之多。

【原理】

考虑构建曼哈顿最小距离生成树时,每个点会与什么样的点连边

以一个每个点为原点建立直角坐标系,将其均分为八个象限

图论 —— 生成树 —— 曼哈顿距离最小生成树_第1张图片

以单个象限 R1 为例:假设以点 A 原点,建立直角坐标系,并在 R1 区域内找任意两点 B(x1,y1)、C(x2,y2),且使得 |AB|<=|BC|

图论 —— 生成树 —— 曼哈顿距离最小生成树_第2张图片

根据 A、B、C 坐标可得:|AB|=x1+y1,|AC|=x2+y2,|BC|=|x1-x2|+|y1-y2|

由于 B、C 都在 R1 区域内,那么 y-x>0 且 x>0,分情况讨论有:

  • x1>x2 且 y1>y2 时:|AB|>|BC|,与假设矛盾
  • x1>x2 且 y1<=y2 时:|BC|=x1-x2+y2-y1,|AC|-|BC|=x2+y2-x1+x2-y2+y1=y1-x1+2*x2,由于 y1>y2>x2>x1,,假设 |AC|<|BC|,则有:x1>2*x2+y1,那么  |AB|=x1+y1>2*x2+2*y1,|AC|=x2+y2<2*x2<|AB|,与前提矛盾,故:|AC|≥|BC|
  • x1<=x2 且 y1>y2 时:|BC|=x2-x1+y1-y2,|AC|-|BC|=x2+y2-x2+x1-y1+y2=x1-y1+2*y2,由于 y1>y2>x2>x1,假设 |AC|<|BC|,则有:y1>2*y2+x1,那么 |AB|=x1+y1>2*x1+2*y2,|AC|=x2+y2<2*y2<|AB|,与前提矛盾,故:|AC|≥|BC|
  • x1<=x2 且 y1<=y2 时:由于 |AB|+|BC|=|AC|,因此有 |AC|>=|BC|

综上,有 |AC|>=|BC|,推广到 8 个象限中可得:以一个每个点为原点建立直角坐标系,将其均分为八个象限,在每象限内只会向距离该点最近的一个点连边。

根据上述结论,可以筛选出每个区域中最近的点。

由于边的双向性,因此只需要针对一个点考虑 R1~R4 四个方向即可,这样总共最多有 4*n 条边,此时再使用 Kruskal 时,时间复杂度仅为 O(nlogn)

考虑点 (x,y) 在 R1~R4 四个象限的条件:

  • 若 (x,y) 在 R1,则:x≥xi,y–x≥yi–xi(最近点的 x+y 最小)
  • 若 (x,y) 在 R2,则:y≥yi,y–x≤yi–xi(最近点的 x+y 最小)
  • 若 (x,y) 在 R3,则:y≤yi,y+x≥yi+xi(最近点的 y–x 最小)
  • 若 (x,y) 在 R4,则:x ≥xi,y+x yi–xi(最近点的 y–x 最小)

因此,利用其中一个条件用排序,另一个条件用树状数组或离散化来维护,从而询问找最近点

【实现】

struct BIT{//树状数组
    int arr[N];
    int Id[N];
    void init(){
        memset(arr,INF,sizeof(arr));
        memset(Id,-1,sizeof(Id));
    }
    int lowbit(int k){
        return k&(-k);
    }
    void update(int pos,int val,int id){
        while(pos>0){
            if(arr[pos]>val){
                arr[pos]=val;
                Id[pos]=id;
            }
            pos-=lowbit(pos);
        }
    }
    int query(int pos,int m){
        int minval=INF;
        int ans=-1;
        while(pos<=m){
            if(minval>arr[pos]){
                minval=arr[pos];
                ans=Id[pos];
            }
            pos+=lowbit(pos);
        }
        return ans;
    }
}B;
struct POS{//区域
    int x,y;
    int id;
    bool operator<(const POS &rhs) const{
        if(x!=rhs.x)
            return x=0;i--){
        int poss=lower_bound(b,b+num,a[i])-b+1;
        int ans=B.query(poss,num);
        if(ans!=-1){//建边
            edge[edgeTot].x=pos[i].id;
            edge[edgeTot].y=pos[ans].id;
            edge[edgeTot].dis=abs(pos[i].x-pos[ans].x)+abs(pos[i].y-pos[ans].y);//曼哈顿距离
            edgeTot++;
        }
        B.update(poss,pos[i].x+pos[i].y,i);
    }
}
void manhattan(int n,int k) {
    for(int dir=1;dir<=4;dir++){//左侧四个区域
        if(dir==2||dir==4){//变换区域
            for(int i=0;i

 

你可能感兴趣的:(#,图论——生成树)