使用Kruskal算法以及用并查集来判断是否有环和用并查集来计算有几个连通块。(我的PTA题目集已经关闭,所以代码没有在PTA中运行计算得分,只在DevC++上进行了运行)。
题目介绍:
代码如下
#include<stdio.h>
#include<stdlib.h>
typedef struct{
int a,b;
int w;
}Road;
Road road[2500];
//定义并查集数组
int v[50];
int find(int a)
{
while(v[a]!=a){
a=v[a];
}
return a;
}
void Kruskal(Road road[],int *sum,int e,int n)
{
int p,q;
for(int i=1;i<=n;i++)
{
v[i]=i;
}
for(int i=0;i<e;i++)
{
p=find(road[i].a);//a,b是两个元素的代表元
q=find(road[i].b);
if(p!=q)//对没有产生回路的边进行操作
{
//这一步有说法的,为了与后面的步骤对应,这里应该让编号靠后的点成为编号靠前的点的孩子结点,而不是反过来。
//这样一个生成树连通块内,编号最小的那个顶点就是对应并查集一个集合中的代表元
v[q]=p;//对并查集进行更新,表示a,b已经加入到同一个生成树中
*sum=*sum+road[i].w;
}
}
//在上面代码的基础上加入一下代码可以用于计算图中的连通块个数(计算并查集中代表元的个数)
int num,x;
int total[50];//用来标记根节点;
for(int i=0;i<50;i++)
{
total[i]=0;
}
for(int i=1;i<=n;i++)
{
x=find(v[i]);
if(total[x]==0)
{
total[x]=1;
num++;
}
}
if(num==1)
{
printf("YES!\nTotal cost: %d",*sum);
}
else
{
printf("NO!\n");
int r=1;
for(int i=1;i<=n;i++)
{
if(total[i]==1)
{
printf("%d part:",r);
for(int j=1;j<=n;j++)
{
if(find(j)==i)
{
printf("%d ",j);
}
}
printf("\n");
r=r+1;
}
}
}
}
int main(){
int n,e;
scanf("%d",&n);
scanf("%d",&e);
for(int i=0;i<e;++i)
{
scanf("%d",&(road[i].a));
scanf("%d",&(road[i].b));
scanf("%d",&(road[i].w));
}
for(int i=0;i<e;i++)
{
for(int j=0;j<e-i-1;j++)
{
if(road[j+1].w<=road[j].w)
{
Road temp=road[j+1];
road[j+1]=road[j];
road[j]=temp;
}
}
}
int sum=0;
Kruskal(road,&sum,e,n);
return 0;
}
运行结果: