村村通工程(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;
}