zoj 1789

 第一次写的代码超时了……这是错误代码
#include<stdio.h>
struct student        //用数组更方便一点儿
{
	int t;
	int father;
}student[30000];
int find(int x)
{
	if(student[x].father==x)
		return x;
	else
		return find(student[x].father);
  //返回树根所代表的学生编号
}
void merge(int x,int y)
{
	student[x].father=y;
}
int main()
{
	int n,m,k,i,x,y,count;
    for(i=0;i<30000;i++)
	{
	    student[i].father=i;
	    student[i].t=0;
	}
	while(scanf("%d%d",&n,&m),!(n==0&&m==0))
	{
		while(m--)  //建立组员间的关系,即集合
		{
			scanf("%d",&k);
			if(k!=1)
			{
				scanf("%d",&x);
				for(i=1;i<k;i++)
				{
					scanf("%d",&y);
					if(student[y].t==0)
					{
						student[y].father=x;
						student[y].t=1;
					}
					else
					{
						if(x!=find(y))
							merge(x,find(y));  //合并集合
					}
				}
			}
		}
		//查并集
		k=find(0);
		for(i=1,count=1;i<n;i++)
		{
			if(k==find(i))   //找到属于同一个子树的元素,count++
				count++;
		}
		printf("%d\n",count);
		for(i=0;i<n;i++)
		{
			student[i].t=0;
			student[i].father=i;
		}
	}
	return 0;
}
/*TLE*/

 

 

改进后的代码

#include<stdio.h>
int find(int *father,int i)
{
	if(father[i]==i)
		return i;
	else
		return find(father,father[i]);
}
int main()
{
	int father[30000],i,k,n,m,x,y,count;
	for(i=0;i<30000;i++)
		father[i]=i;
	while(scanf("%d%d",&n,&m),!(m==0&&n==0))
	{
		while(m--)
		{
		    scanf("%d",&k);
		    if(k!=0)
			{
			    scanf("%d",&x);
			    for(i=1;i<k;i++)
				{
				    scanf("%d",&y);
				    if(father[y]==y)
					    father[y]=x;                       //将 y 的根和 x 连起来
				    else
					{
					    if(find(father,x)!=find(father,y))
					    	father[find(father,y)]=x;    //由于这个merge比较简单,直接写出来也很方便
					}
				}
			}
		}
        k=find(father,0);
		for(i=1,count=1;i<n;i++)
		{
			 if(find(father,i)==k)
			     count++;
		}
		printf("%d\n",count);
		for(i=0;i<n;i++)
			father[i]=i;
	}
	return 0;
}


总结,查并集主要就两个步骤:

建立集合元素间的关系,即合并为一个集合(将根部相连);

void merge(int x,int y)
{
	father[find(father,y)]=y;
}

对每个元素而言查找所属集合。

int find(int *father,int i)
{
	if(father[i]==i)
		return i;
	else
		return find(father,father[i]);
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值