第一题:
此篇是图论的开篇:并查集,简要的说一下并查集的相关知识。
- 定义,并查集的定义是由集合衍生而来,用来表示某些元素是否属于同一集合,如果存在于同一集合,则其拥有共同的祖先。
- 存储结构,存储结构是用双亲表示法来存储,以一维数组的形式存在。
- 合并,并查集在判断集合操作时,只涉及到合并操作,而合并操作,是将其中一棵树的根节点的双亲节点变为另一棵树的根节点。这样两棵树就合并成一棵树了。
- 优化操作,如果只是如上这种合并方法,这是会形成单支树这种特殊情况,而我们用并查集,就是来找根节点的,所以只要我们的目标是寻找根节点,那么我们就可以把并查集进行优化,即路径压缩。防止单支树寻找根节点时间效率过低。
思路: 这道题的想法是找出图的连通分支数,最后有几个连通分支,在这些连通分支上在加上一条路,这时,这个工程就畅通了,所以我们应用并查集,是为了寻找有几个集合,即连通分支数。
注意: 此题思想并不在如何存储图,而是只要之间连接有路,就可以按照并查集来合并。并查集是集合,只与节点有关,与路边等无关。
代码如下:
#include<stdio.h>
#define N 1000
int Tree[N];
int findRoot(int x){
if(Tree[x]==-1)return x;
else{
int tmp=findRoot(Tree[x]);
Tree[x]=tmp;
return tmp;
}
}//并查集模板函数,背住。
int main(){
int n,m;
freopen("in.txt","r",stdin);
while (scanf("%d",&n)!=EOF&&n!=0){
scanf("%d",&m);
for(int i=1;i<=n;i++)Tree[i]=-1;
while(m--){
int a,b;
scanf("%d%d",&a,&b);
a=findRoot(a);
b=findRoot(b);
if(a!=b){
Tree[a]=b;
}//根据题目构建并查集
}
int ans=0;
for(int i=1;i<=n;i++){
if(Tree[i]==-1)ans++;
}
printf("%d\n",ans-1);
}
return 0;
}
第二题:
思路: 此题可以说是并查集的经典应用
代码如下:
#include<stdio.h>
#define N 10000001
int Tree[N];
int findRoot(int x){
if(Tree[x]==-1) return x;
else{
int tmp=findRoot(Tree[x]);
Tree[x]=tmp;
return tmp;
}
}
int sum[N];
int main(){
int n;
freopen("in.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=N;i++){
Tree[i]=-1;
sum[i]=1;
}//初始状态,共n个节点,各自为伍
while(n--){
int a,b;
scanf("%d%d",&a,&b);
a=findRoot(a);
b=findRoot(b);
if(a!=b){
Tree[a]=b;
sum[b]=sum[a]+sum[b];
}
}
int ans=1;
for(int i=1;i<=N;i++){
if(Tree[i]==-1&&sum[i]>ans) ans=sum[i];
}
printf("%d\n",ans);
}
return 0;
}