最小生成树与最小瓶颈路

最小生成树

MSTMSTMST是图GGG最小代价连通子图。

MSTMSTMST具有以下的性质:

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

构造最小生成树的算法:
有两种经典的算法primprimprim算法和kruskalkruskalkruskal算法。它们都基于贪心策略实现。primprimprim是对点进行贪心,所以适用于稠密图。而kruskalkruskalkruskal是对边进行贪心,适用于稀疏图。以下是两种算法的代码实现:

  • primprimprim
    primprimprim与课本上dijkstradijkstradijkstra算法的实现几乎一样。唯一的区别在于更新disdisdis数组的方式。primprimprim是更新加入MST的最小代价,而dijkstradijkstradijkstra是更新路径长度。
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|-1V1个点所以需要∣V∣−1|V|-1V1次循环。在循环内部,需要对disdisdis数组进行遍历O(∣V∣)O(|V|)O(V),标记最小没有加入集合的点。所以时间复杂度为O(∣V∣2)O(|V|^2)O(V2)

  • kruskalkruskalkruskal
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|-1E1条边,其中带路径在压缩的并查集时间复杂度近似为O(1)O(1)O(1)。因为两种操作时并行的。所以选择较大的。所以时间复杂度为O(∣E∣log⁡(∣E∣))O(|E|\log(|E|))O(Elog(E))

最小瓶颈路

def

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

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

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

实现方法

基于prime算法
在求解最小生成树的过程中将有根树建立起来,同时求出所有节点的maxcostmaxcostmaxcost


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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值