并查集 hud1213 poj2236
并查集还是很好理解的,应用在求联通块,儿子们找同一个祖先的问题,现在我并查集代码还有点问题的,具体优化还需要继续学习。
用poj的题目(2236)讲一下就行了把应该。http://poj.org/problem?id=2236
Description
In the process of repairing the network, workers can take two kinds of operations at every moment, repairing a computer, or testing if two computers can communicate. Your job is to answer all the testing operations.
Input
1. "O p" (1 <= p <= N), which means repairing computer p.
2. "S p q" (1 <= p, q <= N), which means testing whether computer p and q can communicate.
The input will not exceed 300000 lines.
Output
Sample Input
4 1 0 1 0 2 0 3 0 4 O 1 O 2 O 4 S 1 4 O 3 S 1 4
Sample Output
FAIL SUCCESS
题目意思是电脑有几个是坏的,给你一个数,表示每个没坏的电脑之间能通信的最大距离,O操作是让一个电脑有用,S问这两个电脑能不能通信,通过别的电脑做桥梁也行。
代码:
#include<iostream>
using namespace std;
//int fa[1005],x[1005],y[1005];
struct ss
{
int x,y,ft,fa;
}s[1005];
int fi(int x)
{
if(s[x].fa==x) return x;//fa[x];
s[x].fa=fi(s[x].fa);
}
void unions(int cs1,int cs2,int juli)
{
int p1=fi(cs1);
int p2=fi(cs2);
if((s[cs1].x-s[cs2].x)*(s[cs1].x-s[cs2].x)+ (s[cs1].y-s[cs2].y)*(s[cs1].y-s[cs2].y)<=juli*juli)
s[p2].fa=p1;
//cout<<"sssssss"<<s[1].fa<<s[2].fa<<s[3].fa<<s[4].fa<<endl;
}
void checks(int x,int y)
{
if(fi(x)!=fi(y))
cout<<"FAIL"<<endl;
else
cout<<"SUCCESS"<<endl;
}
int main( )
{
int i,n,juli,a,b;
char ch;
cin>>n>>juli;
for(i=1;i<=n;i++)
{
s[i].fa=i;
s[i].ft=0;
}
for(i=1;i<=n;i++)
cin>>s[i].x>>s[i].y;
while(cin>>ch)
{
if(ch=='O')
{
cin>>a;
s[a].ft=1;
for(i=1;i<=n;i++)
if(s[i].ft==1&&i!=a)
unions(i,a,juli);
}
else if(ch=='S')
{
cin>>a>>b;
checks(a,b);
}
}
return 0;
}
然后并查集主要是定义一个结构体再写几个函数:
find(避免重名我写成fi)函数:
int fi(int x)
{
if(s[x].fa==x) return x; //fa[x];
s[x].fa=fi(s[x].fa);
}
//这个函数主要的功能是找x的爸爸,如果x的爸爸 s[x].fa 就是他自己的话就返回x自己。如果不是的话,就递归找下去,让x的爸爸等于 他爸爸的爸爸。 认他爷爷做爸爸,如果再不是...........这里需要停下来仔细推导下过程。
union(~unios)函数:
void unions(int cs1,int cs2,int juli)
{
int p1=fi(cs1);
int p2=fi(cs2);
if((s[cs1].x-s[cs2].x)*(s[cs1].x-s[cs2].x)+ (s[cs1].y-s[cs2].y)*(s[cs1].y-s[cs2].y)<=juli*juli)
s[p2].fa=p1;
//cout<<"sssssss"<<s[1].fa<<s[2].fa<<s[3].fa<<s[4].fa<<endl;
}
//这个函数呢,就是联合函数啦,首先!这个int p1=fi(cs1),p2=fi(cs2),是非常重要的,是更新祖先的,之前写我一直忘记,搞的我很难受,后面的if是根据题目需要来写的,上面是如果cs1,cs2这两个点的距离小于题目给的数据,就让cs1和cs2的祖先变成一样的,上面是让cs2的祖先变成cs1的祖先。
check 函数:这个函数相比上面那两个函数就不是那么重要了。也是根据个人习惯和题目来的,写不写无所谓。
void checks(int x,int y)
{
if(fi(x)!=fi(y))
cout<<"FAIL"<<endl;
else
cout<<"SUCCESS"<<endl;
}
然后再说下主函数需要处理的数据初始化。
for(i=1;i<=n;i++)
{
s[i].fa=i;
s[i].ft=0;
}
// 很简单,就是开始让每个人认做他自己的爹,或者说每个人认他自己做爹。s[i].ft的作用是用来标记的。
嗯,应该没有什么问题了。
再贴个模版题,嘻嘻:hdu 1213:
题目就不放了 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1213
代码:
#include<iostream>
using namespace std;
int fa[1005];
int fi(int x)
{
if(fa[x]==x)
return fa[x];
fa[x]=fi(fa[x]);
}
void unio(int x,int y)
{
x=fi(x);
y=fi(y);
if(fa[x]!=fa[y])//if(x!=y)
fa[x]=y;
}
int check(int x,int y)
{
if(fi(x)==fi(y))
return 1;
else
return 0;
}
int main( )
{
int t,i,j,n,m,x,y,ans;
cin >> t;
while(t--)
{
cin>>n>>m;
for(i=1;i<=n;i++)
fa[i]=i;
for(j=0;j<m;j++)
{
cin>>x>>y;
unio(x,y);
//cout << fa[1] <<endl;
}
ans=0;
for(j=1;j<=n;j++)
if(fa[j]==j) ans++;
cout<<ans<<endl;
}
return 0;
}
最后就判断有几个人认他自己做祖先,就有几个联通块了。