原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3371
分析:
题目大意:有n个城市,你可以链接的城市有m对。通过k条输入,可以知道哪些城市是连通的。要求使用最少的花费将所有城市连通起来,如果不能连起来则输出-1,能则输出最小花费。
如下图,通过最后面的k行输入可得下图连通关系。
由m,我们可以知道我们能连接的城市即花费。
城市 城市 花费
1 4 2
2 6 1
2 3 5
3 4 33
所以,为使城市连通又要花费少,我们连接2,6;
算法分析:使用并查集+贪心。连通的城市在一个集合里,对m条可以连接的边安费用从小到大排序。对m连接的连个城市做判断,如果在同一集合则不用连通,不再同一集合,就把集合合并,记录此时花费。m条边判断完后,看是否只有一个集合,是的话则意味着所有此时连通了。
我的代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
#define MAXN 505
#define MAXM 25005
int f[MAXN];
int r[MAXM];
struct Node
{
int p,q,c;
//重定义< 然其按c从小到大排。
bool operator <(const Node &x ) const{
return c<x.c;
}
};
Node a[MAXM];
void init(int n)
{
for(int i=1;i<=n;i++)
{
f[i]=i;
r[i]=1;
}
}
int find(int n) //找n所在集合。
{
if(f[n]==n) return n;
else return f[n]=find(f[n]); //路劲压缩
}
void Union(int x,int y) // x所在集合与y所在集合,合并。
{
int a=find(x);
int b=find(y);
if(a==b) return ;
else
{
if(r[a]<r[b]) //按秩合并,即将集合元素少得合并到元素多得集合里。
{
f[a]=b;
r[b]+=r[a];
}
else
{
f[b]=a;
r[a]+=r[b];
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
init(n); //初始化,并查集。
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a[i].p,&a[i].q,&a[i].c);
}
sort(a,a+m); //对m条边排序。
while(k--) //用并查集,求出集合。
{
int num,e,e1;
scanf("%d%d",&num,&e);
for(int i=1;i<num;i++) //已经输入了a,少输入一个。
{
scanf("%d",&e1);
Union(e,e1);
}
}
int tot=0; //总花费。
for(int i=0;i<m;i++) //在 a 数组中选出可用的边链接集合。
{
int ind1=find(a[i].p);
int ind2=find(a[i].q);
if(ind1==ind2) continue;
else
{
tot+=a[i].c;
Union(ind1,ind2);
}
}
int root_num=0;
for(int i=1;i<=n;i++) //判断集合个数
{
if(f[i]==i) root_num++;
if(root_num>1) break;
}
if(root_num>1) //集合个数>1,即城市没有链接。
{
printf("-1\n");
}
else
{
printf("%d\n",tot);
}
}
return 0;
}
总结:其实本题还是挺简单的,与食物链那种题目相比。