村村通工程(Kruskal算法)/最小生成树Kruskal算法【数据结构】

村村通工程(Kruskal算法)

题目描述

"村村通"是国家一个系统工程,其包涵有:公路、电力、生活和饮用水、电话网、有线电视网、互联网等等。

村村通公路工程,是国家为构建和谐社会,支持新农村建设的一项重大举措,是一项民心工程。又称“五年千亿元”工程

该工程是指中国力争在5年时间实现所有村庄通沥青路或水泥路,以打破农村经济发展的交通瓶颈,解决9亿农民的出行难题。

现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

要求用Kruskal算法求解

输入

第1行:顶点数n

第2行:n个顶点编号

第3行:边数m

接着m行:m条边信息,格式为:顶点1 顶点2 权值

输出

第1行:输出最小生成树的权值之和

接着n-1行对应n-1条边信息

如果能找到最小生成树,按树的生长顺序输出, 边顶点按数组序号升序输出

如果输入数据不足以保证畅通,则直接输出−1,无需输出任何边信息

输入样例1
6
v1 v2 v3 v4 v5 v6
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6
v3 v6 4
v4 v6 2
v5 v6 6

输出样例1
15
v1 v3 1
v4 v6 2
v2 v5 3
v3 v6 4
v2 v3 5

最小生成树Kruskal算法

思路:图中每个顶点各自构成一个连通分量,然后按照边的权值由小到大的顺序.依次考察边集E中的各条边。

若被考察边的两个顶点属于两个不同的连通分量则将此边加人到TE中,同时把两个连通分量连接为一个连通分量;若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小生成树。

#include<bits/stdc++.h>
using namespace std;
struct line
{
    //记录边连接的两个点 且s1的下标比s2的下标小
    string s1,s2;
    //记录边的长度
    int path;
}lines[205];
//将边按照权值从小到大排序
bool cmp(line l1,line l2)
{
    return l1.path<l2.path;
}
//判断两个点是否不在同一个连通分量
bool judge(int home[],int index,map<string,int> m)
{
    string s1=lines[index].s1;
    string s2=lines[index].s2;
    int a1=m[s1];
    int a2=m[s2];
    if(home[a1]==home[a2]) return 0;
    else return 1;
}
//更新home值
void changeHome(int home[],int ini,int fin,int n)
{
    for(int i=0;i<n;i++)
    {
        if(home[i]==ini) home[i]=fin;
    }
}
int main()
{
    int n;
    cin>>n;
    //记录节点
    string s[205];
    //记录节点的下标
    map<string,int> m;
    for(int i=0;i<n;i++) 
    {
        cin>>s[i];
        m[s[i]]=i;
    }

    int line;
    cin>>line;
    for(int i=0;i<line;i++)
    {
        string s1,s2;
        int k;
        cin>>s1>>s2>>k;
        if(m[s1]<m[s2])
        {
            lines[i].s1=s1;
            lines[i].s2=s2;
        }
        else
        {
            lines[i].s1=s2;
            lines[i].s2=s1;
        }
        lines[i].path=k;
    }

    //将边按照权值从小到大排序
    sort(lines,lines+line,cmp);
    //记录所在连通分量 用该连通分量内最小的下标表示
    int home[205];
    for(int i=0;i<n;i++) home[i]=i;
    int index=0;
    int i;
    int res=0;
    string ans="";
    for(i=0;i<n-1;i++)
    {
        //judge判断两个点是否不在同一个连通分量
        while(index<line&&!judge(home,index,m)) index++;
        if(index==line) break;
        int a1=m[lines[index].s1];
        int a2=m[lines[index].s2];
        if(home[a1]>home[a2]) swap(a1,a2);
        //更新home值
        changeHome(home,home[a2],home[a1],n);
        res+=lines[index].path;
        ans=ans+lines[index].s1+" "+lines[index].s2+" "+to_string(lines[index].path)+"\n";
        index++;
    }
    if(i<n-1) cout<<"-1"<<endl;
    else
    {
        cout<<res<<endl;
        cout<<ans;
    }
    return 0;
}

并查集Kruskal(更简便)

思路:在找祖先时使用了并查集的路径压缩

#include<bits/stdc++.h>
using namespace std;
int fa[205];
struct line
{
    string s1,s2;
    int path;
};
bool cmp(line l1,line l2)
{
    return l1.path<l2.path;
}
//使用并查集路径压缩寻找祖先
int find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++) fa[i]=i;
    map<string,int> map;
    string arr[n];
    for(int i=0;i<n;i++)
    {
        cin>>arr[i];
        map[arr[i]]=i;
    }
    int m;
    cin>>m;
    line linearr[m];
    for(int i=0;i<m;i++) 
    {
        cin>>linearr[i].s1>>linearr[i].s2>>linearr[i].path;
        //使s1的编号比s2小
        if(map[linearr[i].s1]>map[linearr[i].s2]) swap(linearr[i].s1,linearr[i].s2);
    }
    sort(linearr,linearr+m,cmp);
    string res="";
    int num=0;
    for(int i=0;i<m;i++)
    {
        string s1=linearr[i].s1;
        string s2=linearr[i].s2;
        int path=linearr[i].path;
        int x=find(map[s1]);
        int y=find(map[s2]);
        if(x!=y) 
        {
            fa[x]=y;
            num+=path;
            res+=s1+" "+s2+" "+to_string(path)+"\n";
        }
    }
    cout<<num<<endl<<res;
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值