题目详情:
假设你是电信工程师,需要为村庄间架设通信网络,使任何两个村庄间都可以实现通信连通(但不一定有直接的快速线路相连,只要互相间接有线路连通即可)。现有规划信息数据,列出了所有可能架设线路的两个村庄及其线路成本,请判断是否可以实现村村互联,如果可以,整个网络的最低成本是多少?如果不能实现村村互联,分成几个部分,各部分有哪些村庄?
输入格式:
第一行给出村庄数目n (1≤n≤50)和候选线路条数m≥0;随后的m行,每行给出3个正整数,分别是该条线路直接连通的两个村庄的编号(编号从1开始起编)以及该线路的建设成本。输出格式: 输出是否实现村村互联的判断结果。 如果是,再输出整个网络的最低成本。
如果不能实现互联,输出分成几部分,每部分有哪些村庄。每个不连通部分的中的顶点是从小到大 。各部分的前后顺序也是按第一个顶点从小到大列出。输入样例: 在这里给出一组输入。例如:
6 10
1 2 6
1 5 10
1 6 12
2 4 5
2 6 8
2 3 3
3 4 7
4 5 9
4 6 11
5 6 16
输出样例:
在这里给出相应的输出。例如:
YES!
Total cost:31
分析:求最小花费,很明显是裸的最小生成树。然后是求具体的联通块,我的想法是 从最小点开始dfs,路径上能够访问到的肯定是一个集合内的,最后排一下序即可。(突然发现既然已经用了并查集,那么可以不用再dfs了。。。)
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int N=1e2+5;
int n,m,f[N],vis[N],cnt;
vector<int>v[N],ans[N];
struct node{
int u,v,w;
}a[N];
//按照边权升序排序
bool cmp(node x,node y){
return x.w<y.w;
}
//路径压缩的并查集
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
void dfs(int x,int no){
for(int i=0;i<v[x].size();i++){
if(!vis[v[x][i]]){
ans[no].push_back(v[x][i]);
vis[v[x][i]]=1;
dfs(v[x][i],no);
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>a[i].u>>a[i].v>>a[i].w;
v[a[i].u].push_back(a[i].v);
v[a[i].v].push_back(a[i].u);
}
int num=0,sum=0;
for(int i=0;i<=n;i++)
f[i]=i;
sort(a,a+m,cmp);
for(int i=0;i<m;i++){
int x=a[i].u,y=a[i].v;
x=find(x),y=find(y);
if(x!=y){//合并不在一个集合内的,更新总代价
f[x]=y;
num++;
sum+=a[i].w;
//已经联通了
if(num>=n-1) break;
}
}
if(num<n-1){
cout<<"NO!"<<endl;
for(int i=1;i<=n;i++){
if(!vis[i]){
vis[i]=1;
ans[cnt].push_back(i);
dfs(i,cnt++);
}
}
for(int i=0;i<cnt;i++){
sort(ans[i].begin(),ans[i].end());
cout<<i+1<<" part:";
for(int j=0;j<ans[i].size();j++){
if(j) cout<<" ";
cout<<ans[i][j];
}
cout<<endl;
}
}else{
cout<<"YES!"<<endl;
cout<<"Total cost:"<<sum<<endl;
}
return 0;
}