最小生成树

给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫做生成说(Spanning Tree)。如果边上有权值,那么使得边权和最小的生成树叫做最小生成树(MST,Minimum Spanning Tree)

例如我们假设有这样一个图:吧顶点看作村庄,边看作计划要修建的道路。未来在所有的村庄间同行,恰好修建村庄数目-1条道路时的情形就对应了一颗生成树。修建道路需要投入建设费,那么求解使得道路建设费用最小的生成树就是最小生成树问题。


最小生成树(权值和17)

常见的求解最小生成树的算法有Kruskal算法和Prim算法。很显然,生成树是否存在和图是否连通是等价的,因此我们嘉定图是连通的。

最小生成树问题1(Prim算法)

Prim算法和Dijkstra算法十分相似,都是从某个顶点出发,不断添加边的算法

T和T以外的顶点之间的边的最小权值

我们令V表示顶点的集合。假设现在已经求得的生成树的顶点的集合是X(包含于V),并且存在在V上的最小生成树使得T是它的一个子图。下面我们证明存在一棵最小生成树使得T是它的一个子图并且它包含了连接X和V\X之间的边中权值最小的边。记链接X和V\X的权值最小的边为e,它连接着V和u。根据假设,存在一棵V上的最小生成树使得T是它的一个子图。如果e也在这棵最小生成树上,问题就得到了证明,所以我们假设e不在这棵树上。因为生成树本质是一棵树,所以在添加了e之后就产生了圈
圈上的边中,必然存在一条和e不同的边f连接着X和V\X。从e的定义可以知道f的权值不会比e小,所以,我们把f从树中删除,然后加上e就可以得到一棵新的生成树,并且总权值不超过原来的生成树。因此可以说存在同时包含e和T的最小生成树。所以把e加入T中满足最初的假设。可以这样不断地加入新的边,知道X=V。因为存在V上的最小生成树使得T是它的一个子图,而X=V,所以T就是V上的最小生成树。
让我们看一下如何查找最小权值的边。把X和顶点V连接的边的最小权值记为mincost[v]。在向X里添加顶点u时,只需要查看和u相连的边就可以了。对于每条边,更新mincost[v]=min(mincost[v],边(u,v)的权值)即可。
如果每次遍历未包含在X中的点的mincost[v],需要O(|V|^2)时间。不过和Dijkstra算法一样,如果用堆来维护mincost时间复杂度就是O(|E|log|V|)。


模板

int cost[MAX_V][MAX_V];//cost[u][v]表示边e=(u,v)的权值(不存在的情况下设为INF) 
int mincost[MAX_V];//从集合X出发的边到每个顶点的最小权值 
bool used[MAX_V];//顶点i是否包含在集合X中 
int V;//顶点数 

int prim(){
    for(int i=0;i<V;i++){
        mincost[i]=INF;
        used[i]=false;
    }
    mincost[0]=0;
    int res=0;
    while(true){
        int v=-1;
        //从不属于X的顶点中选取从X到其权值最小的顶点
        for(int u=0;u<V;u++){
            if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                v=u;
        } 
        if(v==-1)
            break;
        used[v]=true;//把顶点v加入x
        res+=mincost[v];//把边的长度加到结果里
        for(int u=0;u<V;u++){
            mincost[u]=min(mincost[u],cost[v][u]);
        } 
    }
    return res; 
} 

 

最小生成树问题2(Kruskal算法)

Kruskal算法按照边的权值的顺序从小到大查看一遍,如果不产生圈(重边等也算在内),九八当前这条边加入到生成树中。至于这个算法为什么是正确的,其实和Prim算法证明的思路基本相同
接下来我们介绍如何判断是否产生圈。假设现在要把连接顶点u和顶点v的边e加入生成树中。如果加入之前u和v不在同一个连通分量里,那么加入e也不会产生圈。繁殖,如果u和v在同一个连通分量里,那么一定会产生圈。可以使用并查集高效地判断是否始于同一个连通分量。
Kruskal算法在边的排序上最费时,算法的复杂度是O(|E|log|V|)。


模板

struct edge{
    int u,v,cost;
};
bool comp(const edge& e1,const edge& e2){
    return e1.cost<e2.cost;
}
edge es[MAX_E];
int V,E;//顶点数和边数

int Kruskal(){
    sort(es,es+E,comp);//按照edge.cost的顺序从小到大排序 
    init_union_find(V);//并查集的初始化 
    int res = 0;
    for(int i=0;i<E;i++){
        edge e=es[i];
        if(!same(e.u,e.v)){
            unite(e.u,e.v);
            res+=e.cost;
        }
    }
    return res;
} 

 

hiho 1097 最小生成树一·Prim算法

这道题是热身题叭😂

#include <bits/stdc++.h>
using namespace std;
#define maxi 10000
#define INF 9999999
int mincost[maxi],cost[maxi][maxi];
bool used[maxi];
int v,u,n;
int prim(){
    fill(used,used+maxi,false);
    fill(mincost,mincost+maxi,INF);
    mincost[1]=0;
    int res=0;
    while(true){
        int v=-1;
        for(int u=1;u<=n;u++){
            if(!used[u]&&(v==-1||mincost[u]<mincost[v])){
                v=u;
            }
        }
        if(v==-1)
            break;
        used[v]=true;
        res+=mincost[v];
        for(int u=1;u<=n;u++){
            mincost[u]=min(mincost[u],cost[v][u]);
        }
    }
    return res;
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            cin>>cost[i][j];
    }
    cout<<prim();
    return 0;
}

 

HDU 3371 Connect the Cities

考完离散,把这道题AC的感觉真好
这里有几个坑

  • To make it easy, the cities are signed from 1 to n. ---> mincost[i]=cost[1][i];used[1]=true
  • cin会超时,用scanf
  • while(true)要改成for(int i=1;i<n;i++) 我觉得是因为少了一条边
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxi 505 
#define INF 99999999
using namespace std;
int mincost[maxi],cost[maxi][maxi],x[maxi];
bool used[maxi];
int n,m,k,t;

int prim(){
    for(int i=1;i<=n;i++){
        used[i]=false;
        mincost[i]=cost[1][i];//这里题目要求了,看来我是傻了 
    }
    used[1]=true;
    mincost[1]=0;
    int sum=0;
    for(int i=1;i<n;i++){   //好奇怪,这里不能用while(true)因为少了一条边吗 
        int v=-1;
        int mini=INF;
        for(int u=1;u<=n;u++)
            if(!used[u]&&mincost[u]<mini){
                mini=mincost[u];
                v=u;
            }
        if(v==-1)
            return -1;
        used[v]=true;
        sum+=mini;
        for(int u=1;u<=n;u++){
            if(!used[u]&&mincost[u]>cost[v][u]){
                mincost[u]=cost[v][u];
            }
        }
    }
    return sum;
}

int main(){
//  freopen("data","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        int a,b,c;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j)
                    cost[i][j]=0;
                else
                    cost[i][j]=INF;
            }
        }
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&a,&b,&c);
            if(cost[a][b]>c){
                cost[a][b]=cost[b][a]=c;//防止重边 
            } 
        }
        while(k--){
            scanf("%d",&t);
            for(int i=1;i<=t;i++){
                scanf("%d",&x[i]);
            }
            for(int i=1;i<=t-1;i++){
                for(int j=i+1;j<=t;j++)
                    cost[x[i]][x[j]]=cost[x[j]][x[i]]=0;
            }
        }
        cout<<prim()<<endl;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容