关键思路就是并查集,是一种维护集合的数据结构,分别由Union,Find组成。
int Find(int x)//查找父亲结点
{
if (F[x]==x) return x;//找到根结点 并返回
else//未找到 合并
{
int f = Find(F[x]);
F[x]=f;
return f;
}
}
上面就是一个查找函数,而且在查找的过程中增加了剪枝的操作,如果是根节点,就返回,如果不是根节点,就合并当前的结点到根结点。不是单单的搜索操作。
下面就是合并操作了。
void Union(int a,int b)
{
int fa = Find(a);
int fb = Find(b);
if (fa!=fb)//路径不同 合并
{
F[fa] = fb;
}
}
也很简单,如果两个根节点相同,就啥也不做,本身不需要合并,如果不相等,就把其中一个结点的根节点指向另一个根节点。
#include<stdio.h>
int F[10000];
int Find(int x)//查找父亲结点
{
if (F[x]==x) return x;//找到根结点 并返回
else//未找到 合并
{
int f = Find(F[x]);
F[x]=f;
return f;
}
}
void Union(int a,int b)
{
int fa = Find(a);
int fb = Find(b);
if (fa!=fb)//路径不同 合并
{
F[fa] = fb;
}
}
int ww[100000],sum = 0;//查寻人
//写了才发现比想的要简单啊 一遍过 基本上没有错误
int main()
{
int N;scanf ("%d",&N);//部落个数
while (N--)
{
int k;scanf("%d",&k);
for (int i = 0;i<k;i++)
{
int a,b;
scanf ("%d",&a);
if (ww[a]==0)
{
sum++,ww[a]++;
F[a]=a;//初始化自己的结点
}
if (i!=0) Union(a,b);
b=a;
}
}
int Q;scanf ("%d",&Q);
int x = 0;
for (int i = 1;i<=10000;i++)
if (F[i]==i) x++;
printf ("%d %d\n",sum,x);
while (Q--)
{
int a,b;
scanf ("%d%d",&a,&b);
if (Find(a)!=Find(b)) printf ("N\n");
else printf ("Y\n");
}
return 0;
}
然后顺着写,就出来了。
下面再加一个题,也是并查集的知识。
#include<stdio.h>
int F[10000];
int Find(int x)//查找父亲结点
{
if (F[x]==x) return x;//找到根结点 并返回
else//未找到 合并
{
int f = Find(F[x]);
F[x]=f;
return f;
}
}
void Union(int a,int b)
{
int fa = Find(a);
int fb = Find(b);
if (fa!=fb)//路径不同 合并
{
F[fa] = fb;
}
}
int ww[100000];
int main()
{
int N;scanf ("%d",&N);
for (int i = 1;i<=N;i++)//初始化数组 每个根节点都是自己
F[i]=i;
char c;
int a,b;
while (1)
{
scanf ("%c",&c);
if (c=='I')//合并
{
scanf ("%d%d",&a,&b);
Union(a,b);
}
if(c=='C')//查询
{
scanf ("%d%d",&a,&b);
if (Find(a)==Find(b)) printf("yes\n");
else printf ("no\n");
}
if(c=='S')//结束
{
int flag = 1,t=1;
for (int i = 2;i<=N;i++)
{
if (Find(i)!=Find(1))
{
if (!ww[Find(i)])
{
t++;
ww[Find(i)]++;
}//哦豁 并查集写对了!
}
}
if(t==1) printf ("The network is connected.");
else printf("There are %d components.",t);//就是这个查询有多少个联通数的有点小麻烦
break;
}
}
return 0;
}
也没有什么操作,循规循据。