[题解]P1536 村村通
这是一道比较模板的生成树和并查集题,想了一会然后敲出来了,但是因为下标没有从1开始调了半天……
(啊,我这人就爱犯这种错误)
那么,思路是什么呢?
我们发现,每连成1个环,就要多浪费1条边,因为这1条路本来可以往外再连接1个村庄的。
所以,我们需要跑一遍最小生成树,计下cnt,也就是形成环的边的总数。
用m(边的个数)去减cnt,得到的结果就是不形成环的边的总数,存入一个临时变量t中。
这些边经过点的个数就是边的个数+1,即t+1。
最后,用点的总数去减,得到剩下的没有连接的点的个数,输出即可。
附上代码:
1 #include<iostream> 2 using namespace std; 3 struct edge{ 4 int u,v; 5 }edges[500010];//完全图中m=n(n-1)/2 6 int n,m,fa[1010]; 7 int find(int x){//并查集之 查 8 if(fa[x]==x) return x; 9 return fa[x]=find(fa[x]); 10 } 11 int main(){ 12 while(cin>>n){ 13 if(n==0) break; 14 cin>>m; 15 for(int i=1;i<=n;i++) fa[i]=i; 16 for(int i=1;i<=m;i++){ 17 cin>>edges[i].u>>edges[i].v; 18 } 19 int cnt=0; 20 for(int i=1;i<=m;i++){ 21 int x=find(edges[i].u),y=find(edges[i].v); 22 if(x==y){//形成环 23 cnt++; 24 continue; 25 } 26 fa[x]=y;//并查集之 并 27 } 28 int t=m-cnt; 29 cout<<n-t-1<<endl; 30 } 31 return 0; 32 }
其实这一份代码还是有一些缺陷的,比如说完全不需要用结构体数组来存,现读现算即可。可是懒得改啦
个人感觉生成树一类的题目都不用单独去写一个union(合并)函数,直接在主函数中一个语句完事。毕竟生成树都是在两点祖先不同的时候进行合并,既不用再求一次祖先,也不需要单独判断要不要合并。
洛谷的题解中,大部分的思路和这个不太一样。主要是:跑完最小生成树记录多少点的祖先是它本身(祖先是本身就说明这是一个连通块),输出时减去1;也有用桶来存祖先节点,然后遍历输出为1的个数……但除了初始化,这些算法都免不了一两个n次的循环,但我这个算法不需要(嘿嘿)
第一次写题解,感觉很多地方写得不是很清晰,如果有任何想说的请发在评论区,我会认真阅读的。谢谢!