2019 GDUT Winter Training IV ( 图论基础和基础数论 ) I - 畅通工程
原题传送门
专题传送门
题目大意:
有个城镇,给出连通的道路,问最少还需要建设多少条道路?
题目分析:
这题是并查集的模板题,我们先了解一下并查集是什么
并查集其实就分2个部分
第一个部分是寻找祖先,我们需要一个 pre 数组来存每个节点的祖先
int pre[1000];
int find(int x) //查找x的祖先(祖先的祖先是他自己)
{
int r=x;
while (pre[r ]!=r) //如果r的上级不是r自己,r 就接着找他的上级,直到找到祖先为止。
r=pre[r ] ;
return r ; //找到祖先了
}
第二部分是连通
void join(int x,int y) //让祖先不相同的x,y连通,即让x的祖先为y的祖先即可
{
int fx=find(x),fy=find(y);
if(fx!=fy)
pre[fx ]=fy;
}
再来看看find里面的路径压缩优化
int find(int x) //查找根节点
{
int r=x;
while ( pre[r] != r )r=pre[r]; //返回根节点r
int i=x , j ;
while( i != r ) //路径压缩
{
j = pre[ i ]; // 在改变上级之前用临时变量 j 记录下他的值
pre[ i ]= r ; //把上级改为根节点
i=j;
}
return r ;
}
然后我们就可以写出如下的打码。
代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e3+10;
int town[maxn],vis[maxn];
int find(int x){
int temp=x;
while(town[temp]!=temp) temp=town[temp];
int a=x,b;
while(a!=temp){
b=town[a];
town[a]=temp;
a=b;
}
return temp;
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy) town[fx]=fy;
}
int main(){
int n,k,x,y;
while(scanf("%d %d",&n,&k)&&n){
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) town[i]=i;
for(int i=0;i<k;i++){
scanf("%d %d",&x,&y);
merge(x,y);
}
for(int i=1;i<=n;i++) vis[find(i)]=1;
int ans=0;
for(int i=1;i<=n;i++) if(vis[i]) ans++;
printf("%d\n",ans-1);
}
return 0;
}