图论 之 最小生成树 (Kruskal and Prim)

12 篇文章 0 订阅
2 篇文章 0 订阅

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。

生成树是否存在 等价于  图是否连通

JD 1024:畅通工程

题目分析:给图,求最小生成树,Kruskal  和  Prim都试试

Kruskal AC_CODE

const int maxn = 108 ;
const int inf = 1<<30 ;
int n , m ;
struct edge{
    int a , b , cost ;
    bool friend operator<(const edge &A , const edge &B){
        return A.cost < B.cost ;
    }
}e[maxn];
 
int fat[maxn] ;
void init(){
    for(int i = 0;i < maxn;i++){
        fat[i] = i ;
    }
}
int get_Fat(int x){
    if(x == fat[x]) return x ;
    else return fat[x] = get_Fat(fat[x]) ;
}
 
void Merg(int u , int v){
    int fu = get_Fat(u) ;
    int fv = get_Fat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}
bool same(int u , int v){
    int fu = get_Fat(u) ;
    int fv = get_Fat(v) ;
    if(fu == fv) return true ;
    else return false ;
}
 
int main(){
    int i , j , k ;
    //freopen("in.txt" , "r" , stdin) ;
    while(scanf("%d%d",&n, &m) != EOF){
        if(n == 0) break ;
        for(i = 0;i < n;i++){
            scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].cost) ;
        }
        sort(e , e + n) ;
        int ans = 0 ;
        init() ;
        set<int> node ;
        for(i = 0;i < n;i++){
            int a = e[i].a ;
            int b = e[i].b ;
            if(!same(a , b)){
                Merg(a , b) ;
                ans += e[i].cost ;
                node.insert(a) ;
                node.insert(b) ;
            }
        }
        if(node.size() == m) printf("%d\n",ans) ;
        else puts("?") ;
    }
    return 0 ;
}

JD 1017:还是畅通工程

题目分析:与1024一样啦,没什么区别,求最小生成树

Kruskal AC_CODE

const int maxn = 108 ;
const int inf = 1<<30 ;
int n , m ;
struct edge{
    int a , b , cost ;
    bool friend operator<(const edge &A , const edge &B){
        return A.cost < B.cost ;
    }
}e[maxn*maxn/2];
 
int fat[maxn] ;
void init(){
    for(int i = 0;i < maxn;i++){
        fat[i] = i ;
    }
}
int get_Fat(int x){
    if(x == fat[x]) return x ;
    else return fat[x] = get_Fat(fat[x]) ;
}
 
void Merg(int u , int v){
    int fu = get_Fat(u) ;
    int fv = get_Fat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}
bool same(int u , int v){
    int fu = get_Fat(u) ;
    int fv = get_Fat(v) ;
    if(fu == fv) return true ;
    else return false ;
}
 
int main(){
    int i , j , k ;
    //freopen("in.txt" , "r" , stdin) ;
    while(scanf("%d",&n) != EOF){
        if(n == 0) break ;
        int m = n*(n-1)/2 ;
        for(i = 0;i < m;i++){
            scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].cost) ;
        }
        sort(e , e + m) ;
        int ans = 0 ;
        init() ;
        for(i = 0;i < m;i++){
            int a = e[i].a ;
            int b = e[i].b ;
            if(!same(a , b)){
                Merg(a , b) ;
                ans += e[i].cost ;
            }
        }
        printf("%d\n",ans) ;
    }
    return 0 ;
}

JD 1012:畅通工程

题目分析:就是并查集的应用啦,判断有几个等价类

AC_CODE

const int maxn = 1008 ;
int fat[maxn] ;
int n , m ;
void init(){
    for(int i = 0;i < maxn;i++)
        fat[i] = i ;
}
int GetFat(int x){
    if(x == fat[x]) return x ;
    else fat[x] = GetFat(fat[x]) ;
}
 
void Merg(int u , int v){
    int fu = GetFat(u) ;
    int fv = GetFat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}
int main(){
    //freopen("in.txt","r",stdin) ;
    int i , j , k , u , v ;
    while(cin >> n){
        if(n == 0) break ;
        cin >> m ;
        init() ;
        for(i = 0;i < m;i++){
            scanf("%d%d",&u,&v) ;
            Merg(u , v) ;
        }
        set<int> s ;
        for(i = 1;i <= n;i++){
            s.insert(GetFat(i)) ;
        }
        printf("%d\n",s.size() - 1) ;
    }
}

JD 1028:继续畅通工程

题目分析:最小生成树,用Kruskal

Kruskal AC_CODE

const int maxn = 108 ;
const int inf = 1<<30 ;
int n , m ;
struct edge{
    int a , b , cost , flag;
    bool friend operator<(const edge &A , const edge &B){
        if(A.flag == B.flag) return A.cost < B.cost ;
        return A.flag > B.flag ;
    }
}e[maxn*maxn/2];
 
int fat[maxn] ;
void init(){
    for(int i = 0;i < maxn;i++){
        fat[i] = i ;
    }
}
int get_Fat(int x){
    if(x == fat[x]) return x ;
    else return fat[x] = get_Fat(fat[x]) ;
}
 
void Merg(int u , int v){
    int fu = get_Fat(u) ;
    int fv = get_Fat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}
bool same(int u , int v){
    int fu = get_Fat(u) ;
    int fv = get_Fat(v) ;
    if(fu == fv) return true ;
    else return false ;
}
 
int main(){
    int i , j , k ;
    //freopen("in.txt" , "r" , stdin) ;
    while(scanf("%d",&n) != EOF){
        if(n == 0) break ;
        int m = n*(n-1)/2 ;
        for(i = 0;i < m;i++){
            scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].cost,&e[i].flag) ;
        }
        sort(e , e + m) ;
        int ans = 0 ;
        init() ;
        for(i = 0;i < m;i++){
            int a = e[i].a ;
            int b = e[i].b ;
            if(!same(a , b)){
                Merg(a , b) ;
                ans += e[i].flag ? 0 : e[i].cost ;
            }
        }
        printf("%d\n",ans) ;
    }
    return 0 ;
}

JD 1347:孤岛连通工程

题目分析:就是求最小生成树

KruskalAC_CODE

typedef long long LL ;
const int inf = 1<<30 ;
const int maxn = 1008 ;
int n , m ;
int fat[maxn] ;
struct Node{
    int u , v , d ;
    Node(){}
    Node(int a , int b , int c):u(a),v(b),d(c){}
    bool friend operator<(const Node &A , const Node &B){
        return A.d < B.d ;
    }
}edge[maxn*10];
void init(){
    for(int i = 1;i <= n;i++)
        fat[i] = i ;
}
int GetFat(int x){
    if(x == fat[x]) return x ;
    else return fat[x] = GetFat(fat[x]) ;
}
bool Same(int u ,int v){
    int fu = GetFat(u) ;
    int fv = GetFat(v) ;
    if(fu == fv) return true ;
    else return false ;
}
 
void Merge(int u ,int v){
    int fu = GetFat(u) ;
    int fv = GetFat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}
 
int main(){
    int i , j , k , u , v , d;
    //freopen("in.txt","r",stdin) ;
    while(scanf("%d%d",&n,&m) != EOF){
        for(i = 0;i < m;i++){
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].d) ;
        }
        sort(edge , edge + m) ;
        int ans = 0 ;
        Node e ;
        init() ;
        int cnt = 0 ;
        for(i = 0;i < m;i++){
            e = edge[i] ;
            if(!Same(e.u , e.v)){
                ans += e.d ;
                Merge(e.u , e.v) ;
                cnt++ ;
            }
        }
        if(cnt < n - 1) puts("no") ;
        else printf("%d\n",ans) ;
    }
}

JD 1545:奇怪的连通图

题目分析:这里不是求最小生成树,只要求1与n连通,且使得边长的最大值最小。可利用Kruskal算法思想,1到n连通即可,无须构成最小生成树。

AC_CODE

const int maxn = 10008 ;
const int maxm = 100008 ;
int fat[maxn] ;
int n , m ;
struct edge{
    int u , v , d ;
    friend bool operator<(const edge &A , const edge &B){
        return A.d < B.d ;
    }
}e[maxm];

void init(){
    int i ;
    for(i = 1;i <= n;i++)
        fat[i] = i ;
}

int getFat(int x){
    if(x == fat[x]) return x ;
    else return fat[x] = getFat(fat[x]) ;
}

bool same(int u , int v){
    int fu = getFat(u) ;
    int fv = getFat(v) ;
    return fu == fv ;
}

void Merg(int u , int v){
    int fu = getFat(u) ;
    int fv = getFat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}

int main(){
    int i  , j , k ;
    //freopen("in.txt","r",stdin) ;
    while(scanf("%d%d",&n,&m) != EOF){
        for(i = 0;i < m;i++)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].d) ;
        sort(e , e + m) ;
        init() ;
        for(i = 0;i < m;i++){
            if(!same(1 , n)){
                Merg(e[i].u , e[i].v) ;
                k = e[i].d ;
            }
            else break ;
        }
        if(same(1 , n)) printf("%d\n",k) ;
        else puts("-1") ;
    }
    return 0 ;
}

JD 1417:变型金刚

题目分析:与1545类似,只不过这里要构成最小生成树。还有,输入时结点的编号用字符串表示的,要hash成整数。其他都一样了。。。

Kruskal AC_CODE

const int maxn = 108 ;
const int maxm = 10008 ;
int fat[maxn] ;
int n , m ;
map< string , int > mp ;
struct edge{
    int u , v , d ;
    edge(){}
    edge(int a , int b , int c):u(a),v(b),d(c){}
    friend bool operator<(const edge &A , const edge &B){
        return A.d < B.d ;
    }
}e[maxm];

void init(){
    int i ;
    for(i = 1;i <= n;i++)
        fat[i] = i ;
}

int getFat(int x){
    if(x == fat[x]) return x ;
    else return fat[x] = getFat(fat[x]) ;
}

bool same(int u , int v){
    int fu = getFat(u) ;
    int fv = getFat(v) ;
    return fu == fv ;
}

void Merg(int u , int v){
    int fu = getFat(u) ;
    int fv = getFat(v) ;
    if(fu != fv){
        fat[fu] = fv ;
    }
}

int main(){
    int i  , j , k , a , b , c ;
    //freopen("in.txt","r",stdin) ;
    string s ;
    while(scanf("%d%d",&n,&m) != EOF){
        mp.clear() ;
        k = 1 ;
        for(i = 0;i < m;i++){
            cin >> s ;
            if(mp.find(s) == mp.end())
                mp[s] = k++ ;
            a = mp[s] ;
            cin >> s ;
            if(mp.find(s) == mp.end())
                mp[s] = k++ ;
            b = mp[s] ;
            scanf("%d",&c) ;
            e[i] = edge(a , b , c) ;
        }
        sort(e , e + m) ;
        init() ;
        int t = 0 ;
        for(i = 0;i < m;i++){
            if(!same(e[i].u , e[i].v)){
                Merg(e[i].u , e[i].v) ;
                k = e[i].d ;
                t++ ;
            }
        }
        if(t == n - 1) printf("%d\n",k) ;
        else puts("My God") ;
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值