1393:联络员(liaison)
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
Tyvj已经一岁了,网站也由最初的几个用户增加到了上万个用户,随着Tyvj网站的逐步壮大,管理员的数目也越来越多,现在你身为Tyvj管理层的联络员,希望你找到一些通信渠道,使得管理员两两都可以联络(直接或者是间接都可以)。Tyvj是一个公益性的网站,没有过多的利润,所以你要尽可能的使费用少才可以。
目前你已经知道,Tyvj的通信渠道分为两大类,一类是必选通信渠道,无论价格多少,你都需要把所有的都选择上;还有一类是选择性的通信渠道,你可以从中挑选一些作为最终管理员联络的通信渠道。数据保证给出的通行渠道可以让所有的管理员联通。
【输入】
第一行n,m表示Tyvj一共有n个管理员,有m个通信渠道;
第二行到m+1行,每行四个非负整数,p,u,v,w 当p=1时,表示这个通信渠道为必选通信渠道;当p=2时,表示这个通信渠道为选择性通信渠道;u,v,w表示本条信息描述的是u,v管理员之间的通信渠道,u可以收到v的信息,v也可以收到u的信息,w表示费用。
【输出】
最小的通信费用。
【输入样例】
5 6
1 1 2 1
1 2 3 1
1 3 4 1
1 4 1 1
2 2 5 10
2 2 5 5
【输出样例】
9
【提示】
【样例解释】
1-2-3-4-1存在四个必选渠道,形成一个环,互相可以到达。需要让所有管理员联通,需要联通2号和5号管理员,选择费用为5的渠道,所以总的费用为9。
【注意】
U,v之间可能存在多条通信渠道,你的程序应该累加所有u,v之间的必选通行渠道
【数据范围】
对于30%的数据,n≤10,m≤100;
对于50%的数据, n≤200,m≤1000
对于100%的数据,n≤2000,m≤10000
1. #include <iostream> 2. #include <stdio.h> 3. #include <algorithm> 4. using namespace std; 5. const int maxn=2010; // 最大点数 6. const int maxm=20010; // 最大边数 7. struct point{ 8. int x,y,c; // 无向边(x,y),权值为c 9. }a[maxm]; // 存储询问的边 10. bool cmp(const point &a,const point &b){ 11. return a.c<b.c; 12. } 13. int n,m,ans,k; // n为点数,m为边数(包括询问边),ans为最小生成树的边权和,k为询问边的数量。 14. int f[maxn]; // 并查集,用于判断两个节点是否在同一集合 15. int find(int x){ // 查找x所属集合的代表元 16. if(f[x]==x) return x; 17. return f[x]=find(f[x]); // 路径压缩 18. } 19. void unionn(int x,int y){ // 将x和y所属集合合并 20. int a=find(x); 21. int b=find(y); 22. if(a!=b) f[a]=b; 23. } 24. int main(){ 25. scanf("%d %d",&n,&m); 26. for(int i=1;i<=n;i++) f[i]=i; // 并查集初始化,每个节点的父亲为自己 27. int p,u,v,w; 28. for(int i=1;i<=m;i++){ 29. scanf("%d %d %d %d",&p,&u,&v,&w); 30. if(p==1){ // 如果该边是连通操作,则将u和v所在的集合合并 31. unionn(u,v);ans+=w; 32. } 33. else{ // 否则,将该边保存到a数组中,用于后续的Kruskal算法构造最小生成树 34. k++;a[k].x=u;a[k].y=v;a[k].c=w; 35. } 36. } 37. sort(a+1,a+k+1,cmp); // 对询问边按照权重进行排序 38. for(int i=1;i<=k;i++){ 39. if(find(a[i].x)!=find(a[i].y)){ // 如果a[i]的两个端点不在同一个集合中,则可以选择使用这条边,将它们合并,并增加对应的权值。 40. unionn(a[i].x,a[i].y); 41. ans+=a[i].c; 42. } 43. } 44. printf("%d",ans); // 输出最小生成树的权值和 45. return 0; 46. }