1.团伙(group.pas/c/cpp)
AYYZOJ p1948
题目分析:
题目大意是有n个人求最多可能有几个团伙,由题意很容易想到用并查集解决。我们可以先假设每个人都不认识,在这一前提下可以保证团伙最多。在处理朋友的时候,把所有是朋友的人加入同一团伙;处理两人是敌人的时候,需要事先记录下每个人的敌人,然后依次把敌人的敌人加入同一团伙。最后统计有多少个团伙即可。
1 var d:array[0..1000,0..1000] of longint; //d[i][j]存储第i个人的第j个敌人 2 f:array[0..1000] of longint; 3 i,j,m,n,x,y,k,fx,fy,ans:longint; 4 function get_father(x:longint):longint; 5 begin 6 if f[x]=0 then 7 begin 8 get_father:=x; 9 end else 10 begin 11 get_father:=get_father(f[x]); 12 f[x]:=get_father; 13 end; 14 end; 15 16 begin 17 fillchar(f,sizeof(f),0); 18 fillchar(d,sizeof(d),0); 19 readln(n,m); 20 for i:=1 to m do 21 begin 22 readln(k,x,y); 23 fx:=get_father(x); fy:=get_father(y); 24 if k=0 then //如果x,y是朋友则合并。 25 begin 26 if fx<>fy then f[y]:=fx; 27 end; 28 if k=1 then 29 begin 30 inc(d[x,0]); d[x,d[x,0]]:=y; //d[x,0]记录x的敌人数 31 inc(d[y,0]); d[y,d[y,0]]:=x; //d[y,0]记录y的敌人数 32 //如果x,y是敌人,则把x并入y的所有敌人中,y并入x的敌人中 33 for j:=1 to d[y,0] do 34 if fx<>get_father(d[y,j]) then f[d[y,j]]:=fx; 35 for j:=1 to d[x,0] do 36 if fy<>get_father(d[x,j]) then f[d[x,j]]:=fy; 37 end; 38 end; 39 ans:=0; 40 for i:=1 to n do 41 if f[i]=0 then inc(ans); //如果自己就是树的根表明是一个集合 42 writeln(ans); 43 end.
1 var 2 a:array[1..1000,0..1000]of longint; 3 i,j,k,n,m,t,s:longint; 4 fa:array[1..1000]of longint; 5 function getfa(x:longint):longint; 6 begin 7 if fa[x]=0 then getfa:=x 8 else 9 begin 10 fa[x]:=getfa(fa[x]); 11 getfa:=fa[x]; 12 end; 13 end; 14 procedure union(x,y:longint); 15 var i,j:longint; 16 begin 17 i:=getfa(x); 18 j:=getfa(y); 19 if i<>j then fa[i]:=j; 20 end; 21 begin 22 readln(n,m); 23 for i:=1 to m do 24 begin 25 readln(j,k,t); 26 if j=0 then union(k,t) 27 else begin 28 inc(a[k,0]); 29 a[k,a[k,0]]:=t; 30 inc(a[t,0]); 31 a[t,a[t,0]]:=k; 32 end; 33 end; 34 for i:=1 to n do 35 for j:=1 to a[i,0] do 36 union(a[i,1],a[i,j]); 37 for i:=1 to n do 38 if getfa(i)=i then inc(s); 39 writeln(s); 40 end.
我比较倾向于参考程序2,程序1有点不理解。
2.打击犯罪(black.pas/c/cpp)
AYYZOJ p1949
分析:
因为打击犯罪的顺序为由前到后依次打击,很容易想到可以倒序合并(为什么我想不到,太弱了。。),一旦某一团伙的危险程度>n/2,停止输出即可。
至于团伙的危险等级的确认,可以新建一个count数组,在合并时累加即可。
下面分析为转载:来源
此题首先要理解题意,题意意思是按顺序删点,如果删掉k点后,1~k-1的点也被删掉这样就好解决了,那怎么和并查集联系起来呢?我们可以这样理解,我从1开始删点如当前删了点k这对大于k的点进行并查集,并得时候更新根节点的节点数,如果最大的节点数大于(n+1)/2则继续删点,直到最大集合节点数小于等于(n+1)/2为止,但这样做每删一个点,并查集就得重新计算一次,那我们换一个思路,我们倒过来,我们不删点,我们求删点完后剩下的图组成的集合,我假设删点删到了k那剩下的图就是由k~n之间的的点组成如果他们组成的结合最大子图节点数不大(n+1)/2则说明k不该删,则继续往前推。
1 var 2 i,j,k,l,m,n:longint; 3 f,c:array[1..1000] of longint; //count数组 4 g:array[1..1000] of boolean; 5 a:array[0..1000,0..1000] of longint; 6 function getfa(x:longint):longint; 7 begin 8 if f[x]=0 then exit(x); 9 f[x]:=getfa(f[x]); 10 getfa:=f[x]; 11 end; 12 procedure together(x,y:longint); 13 var i,j:longint; 14 begin 15 i:=getfa(x); 16 j:=getfa(y); 17 if i<>j then 18 begin 19 f[i]:=j; 20 c[j]:=c[i]+c[j]; 21 c[i]:=0; 22 end; 23 end; 24 begin 25 readln(n); 26 for i:=1 to n do c[i]:=1; 27 for i:=1 to n do 28 begin 29 read(a[i,0]); 30 for j:=1 to a[i,0] do read(a[i,j]); 31 end; 32 for i:=n downto 1 do 33 begin 34 for j:=1 to a[i,0] do 35 if a[i,j]>i then 36 together(i,a[i,j]); 37 l:=0; 38 for j:=1 to n do if c[j]>n div 2 then l:=1; 39 if l=1 then 40 begin 41 writeln(i); 42 break; 43 end; 44 end; 45 end.