最小生成树与最小瓶颈路

最小生成树

M S T MST MST是图 G G G最小代价连通子图。

M S T MST MST具有以下的性质:

  1. 最小生成树不唯一,但是它们的边权和相同
  2. 最小生成树的边数等于顶点数减一(树的性质)

构造最小生成树的算法:
有两种经典的算法 p r i m prim prim算法和 k r u s k a l kruskal kruskal算法。它们都基于贪心策略实现。 p r i m prim prim是对点进行贪心,所以适用于稠密图。而 k r u s k a l kruskal kruskal是对边进行贪心,适用于稀疏图。以下是两种算法的代码实现:

  • p r i m prim prim
    p r i m prim prim与课本上 d i j k s t r a dijkstra dijkstra算法的实现几乎一样。唯一的区别在于更新 d i s dis dis数组的方式。 p r i m prim prim是更新加入MST的最小代价,而 d i j k s t r a dijkstra dijkstra是更新路径长度。
int prim(){
    int res=0,cnt=0;	//记录路径长度和边的数量
    bool vis[maxn];
    int dis[maxn];
    dis[1] = 0;
    for(int i=0;i<n;i++){
        int mx = inf, index = -1;
        for(int j=1;j<=n;j++){		//标记下一个加入MST的点
            if(!vis[j]&&mx>dis[j])
                mx = dis[j] , index = j;
        }
        if(index==-1) break;		//如果不存在,跳出循环
        for(int j=1;j<=n;j++){		//用标记过的点更新其他没有加入MST的点
            if(!vis[j]){	//邻接矩阵存图
                dis[j] = min(g[index][j],dis[j]);	//更新加入MST的最小代价
            }
        }
        vis[index] = true;
        res += dis[index];
        cnt++;
    }
    if(cnt!=n-1){		//如果边数不是n-1,则不能构成MST
        cout<<"不能构成MST"<<endl;
        return -1;
    }
    else
        return res;
}

时间复杂度分析:一共要贪心 ∣ V ∣ − 1 |V|-1 V1个点所以需要 ∣ V ∣ − 1 |V|-1 V1次循环。在循环内部,需要对 d i s dis dis数组进行遍历 O ( ∣ V ∣ ) O(|V|) O(V),标记最小没有加入集合的点。所以时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)

  • k r u s k a l kruskal kruskal
struct Edge {
    int u,v,w;
};
Edge e[200005];		//存边
int fa[5005],n,m,ans,eu,ev,cnt;
bool cmp(const Edge& a, const Edge& b){
    return a.w<b.w;
}
int find(int x){	//并查集
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];
    return x;
}
void kruskal(){
    sort(e,e+m,cmp);	//对边排序,便于后面贪心
    for(int i=0;i<m;i++) {
        eu = find(e[i].u),ev = find(e[i].v);
        if(eu==ev) continue;	
        //如果都在一个集合里面不需要合并(不一定是在MST中,有可能存在多个集合还没有合并)
        ans+=e[i].w;
        fa[eu]=ev;	//合并集合
        if(++cnt==n-1) break;
    }
}

时间复杂度分析:首先要对 ∣ E ∣ |E| E条边排序 O ( ∣ E ∣ log ⁡ ∣ E ∣ ) O(|E|\log{|E|}) O(ElogE)。然后需要贪心选择 ∣ E ∣ − 1 |E|-1 E1条边,其中带路径在压缩的并查集时间复杂度近似为 O ( 1 ) O(1) O(1)。因为两种操作时并行的。所以选择较大的。所以时间复杂度为 O ( ∣ E ∣ log ⁡ ( ∣ E ∣ ) ) O(|E|\log(|E|)) O(Elog(E))

最小瓶颈路

def

给定一个加权无向图两个节点 u u u v v v,求 u u u v v v的一条路径,使得路径上边的最大权值最小

首先,任意两个节点的最小瓶颈路一定在最小生成树上。所以我们可以先求出最小生成树。然后从 u u u点DFS到 v v v点,这样实现的时间复杂度是 O ( log ⁡ n ) O(\log{n}) O(logn)。显然会TLE。

所以我们考虑在查询之前进行预处理,将所有节点对的最长边保存在一个 m a x c o s t maxcost maxcost数组中,然后查询只要KaTeX parse error: Expected 'EOF', got '}' at position 4: O(1}̲)。递推公式如下:
m a x c o s t [ j ] [ u ] = m a x ( m a x c o s t [ j ] [ f a [ u ] ] , m a x c o s t [ j ] [ u ] ) maxcost[j][u] = max(maxcost[j][fa[u]],maxcost[j][u]) maxcost[j][u]=max(maxcost[j][fa[u]],maxcost[j][u])
更新 j j j u u u的最小瓶颈路是 j j j f a [ u ] fa[u] fa[u] j j j u u u最小瓶颈路的最大值。

实现方法

基于prime算法
在求解最小生成树的过程中将有根树建立起来,同时求出所有节点的 m a x c o s t maxcost maxcost


int prime(){
    int res = 0;
    memset(maxcost,0,sizeof(maxcost));
    for(int i=1;i<=n;i++){
        vis[i]=0 , d[i] = INF , pre[i] = i;
    }
    d[s] = 0;                           //预先选中s
    for(int i=0;i<n;i++){
        int mx = INF , index = -1;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&d[j]<mx)
                mx = d[index=j];       //选中d最小的点
        }
        if(index==-1) break;
        for(int j=1;j<=n;j++)    //j->index = j->fa->index
            if(vis[j])
                maxcost[index][j] = maxcost[j][index] = 
                    max(maxcost[pre[index]][j],mx);
        res += mx;
        vis[index] = 1;
        for(int j=1;j<=n;j++){         //用选中的index更新其他节点
            if(!vis[j]&&g[index][j]<d[j]){
                d[j] = g[index][j];
                pre[j] = index;
            }
        }
    }
    return res;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值