G - 还是畅通工程
题目连接 http://acm.hust.edu.cn/vjudge/contest/123674#problem/G
看到题目的时候是懵逼的,把图一画出来发现就是求最小生成树(现在才觉得离散数学挺重要的),按权值从大到小排序,
n个点需要添n-1条边。最开始的思路是排序后满足条件的始点和终点最多只有一个出现过一次,不知道是思路不对还是其
他的总是WA.后来又看书发现书上有模板,运用了并查集,将每个连通分量看成一个集合,集合包含了连通分量里所有的点,
所有的点都两两想通。
/*假设i条边的端点序号和权值分别保存在u[i],v[i],w[i], r[i]代表中的i代表边的序号(间接排序,排序的关键字是对象的代号 而不是对象本身)*/ int com(const int i,const int j) { return w[i]<w[j];(升序排列) w[i]>w[j](降序排列) } int find(int x) { return p[x]==x?x:p[x]=(find(p[x]))//简洁的代码,压缩了路径 } int Kruskal() { int ans,i; for(i=0;i<n;i++) p[i]=i;//点是从0开始的,初始情况下每个根节点都是本身 for(i=0;i<m;i++) r[i]=i;//初始化边序号 sort(r,r+m,cmp); for(i=0;i<m;i++) { int e=r[i]; int x=find(u[e]);int y=(find(v[e])); if(x!=y) { ans+=w[e]; p[x]=y; } } }//注意不能写成p[u[e]]=p[v[e]],因为u[e],v[e],可能不是树根
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 105 #define Ma 5560 int ptr[N]; int b[Ma],e[Ma],qu[Ma],r[Ma]; int com(const int i,const int j) { return qu[i]<qu[j];//升序排列 } int find(int x) { return ptr[x]==x?x:ptr[x]=find(ptr[x]); } void init () { //memset(ptr,0,sizeof(ptr)); memset(b,0,sizeof(b)); memset(e,0,sizeof(e)); memset(qu,0,sizeof(qu)); memset(r,0,sizeof(r)); } int Kruskal(int n,int bi) { int ans=0; int i,j; for(i=0;i<n;i++) ptr[i]=i; for(j=0;j<bi;j++) r[j]=j; sort(r,r+bi,com);//根据权值给边的序号排序 for(i=0;i<bi;i++) { int c=r[i];int x=find(b[c]);int y=find(e[c]); if(x!=y) { ans+=qu[c]; ptr[x]=y; } } return ans; } int main() { int n,bi; while(scanf("%d",&n)&&n) { init(); bi=n*(n-1)/2; for( int i=0;i<bi;i++) { scanf("%d %d %d",&b[i],&e[i],&qu[i]); b[i]--;//此处容易忽略。点是从1-n e[i]--;//而上面的函数却都是从0开始到n-1结束 } printf("%d\n",Kruskal(n,bi)); } return 0; }