用树实现并查集(包括路径压缩和启发式合并)
二、犯罪团伙
group.pas/c/cpp/group.in/group.out
【问题描述:】
警察抓到了n个罪犯,警察根据经验知道他们属于不同的犯罪团伙,却不能判断有多少个团伙,但通过警察的审讯,知道其中的一些罪犯之间相互认识,已知同一犯罪团伙的成员之间直接或间接认识。有可能一个犯罪团伙只有一个人。请你根据已知罪犯之间的关系,确定犯罪团伙的数量。已知罪犯的编号从1至n。
【输入】:
第一行:n(<=10000,罪犯数量),
第二行:m(<100000,关系数量)
以下若干行:每行两个数:I 和j,中间一个空格隔开,表示罪犯i和罪犯j相互认识。
【输出】
一个整数,犯罪团伙的数量。
【样例输入】
11
8
1 2
4 5
3 4
1 3
5 6
7 10
5 10
8 9
【样例输出】
3
说明:共三个集团。
1.floodfill 慢
2.way[x,y]一个关系两个点 太大
3.并查集
find(x)
if f[x]=0 exit(x)
find=find(f[x])
f[x]=find{路径压缩,某的老大就是最大的老大}
对于 x,y bossx=find(x) bossy=find(y)
= 无视
<> f[bossx]=bossy{并集}
在网上又看了一个启发式合兵,建一个rank数组初始为0,相等则将作最大老大的的结点的rank+1,不相等就把rank大的大老大作最大老大,详见题。。。
自己做的:
program group;
Var
i,j,k,n,m,c:integer;
f:array[0..10000] of integer;
r:array[0..10000] of integer;
function find(a:integer):integer;
begin
if f[a]=a then
find:=a else begin
find:=find(f[a]);
f[a]:=find;
end;
end;
procedure un(x,y:integer);
var fx,fy:integer;
begin
fx:=find(x);fy:=find(y);
if fx<>fy then
begin
if r[fx]<r[fy] then
f[fx]:=fy
else
begin
f[fy]:=fx;
if r[fx]=r[fy] then
inc(r[fx]);
end;
end;
end;
begin
assign(input,'group.in');
assign(output,'group.out');
reset(input);
rewrite(output);
readln(n);
readln(m);
fillchar(r,sizeof(r),0);
for k:=1 to n do
f[k]:=k;
for k:=1 to m do begin
readln(i,j);
un(i,j);
end;
for k:=1 to n do
if f[k]=k then
inc(c);
writeln(c);
close(input);
close(output);
end.