最近因为保研,在刷一些poj上面的ACM题,发现智商真的是无限被碾压。。。
1308 这一道题目考察的是并查集,并查集题目,并的意思就是将两个不同类别的集合合并到一起,类似于两棵树合并;查的意思就是找到这个点所属集合的根节点。基本上并查集题目都是在大体架构上面加一些东西即可。并查集代码模板在这里点击打开链接
并查集的基本模板:
int pre[1000 ];
int find(int x) //查找根节点
{
int r=x;
while ( pre[r ] != r ) //返回根节点 r
r=pre[r ];
int i=x , j ;
while( i != r ) //路径压缩
{
j = pre[ i ]; // 在改变上级之前用临时变量 j 记录下他的值
pre[ i ]= r ; //把上级改为根节点
i=j;
}
return r ;
}
void join(int x,int y) /判断x y是否连通,
//如果已经连通,就不用管了 //如果不连通,就把它们所在的连通分支合并起,
{
int fx=find(x),fy=find(y);
if(fx!=fy)
pre[fx ]=fy;
}
上面是并查集的并查操作,也是最基本的模板,一般情况下我们只需要加入一些记录数组或者判断变量,就可以很快做出并查集的题目。
好了,现在开始讨论这道题目。
这道题目有三个判断是否是树的地方:
1. 为空节点(一开始就是0 0 )
2. 当前两个节点的根结点是不是一个,(也就是没有出现了5-3-8,5-6 ,这是两个节点是6 8的情况。)
3. 最后没有独立节点在树外(最后判断所有点的根结点是否一样)
如果满足了这三点,这个就是一个树,反之不是。所以我们加一个ranks数组记录所有被提到的点,main函数中加一个tree的布尔值来判断是否这一个例子是否是树。基本架构还是并查集的模板。
#include <iostream>
using namespace std;
const int MAXN = 105;
int pa[MAXN];
int ranks[MAXN];
void make_set()
{/*创建一个单元集*/
int x;
for(x=0;x<100;x++){
pa[x] = x;
ranks[x] = 0;
}
}
int find_set(int x)
{/*带路径压缩的查找*/
/*保存待查找的数*/
int r = x, temp;
/*找到根节点*/
while(pa[r] != r) r = pa[r];
while(x != r)
{
temp = pa[x];
pa[x] = r;
x = temp;
}
return x;
}
void union_set(int x, int y)
{
x = find_set(x);
y = find_set(y);
if(x == y)return ;
pa[y]=x;
}
int main()
{
int x, y, i,temp,size=1;
make_set();
while(1){
scanf("%d %d",&x,&y);
if(x==-1 && y==-1)break;
if(x==0 && y==0){
printf("Case %d is a tree.\n",size++);
continue;
}
make_set();
ranks[x] = ranks[y] = true;
bool tree = true;
temp=x;
if(x==y) tree = false;
else union_set(x,y);
while(scanf("%d %d",&x,&y) && x!=0){
ranks[x] =ranks[y]=true;
if(find_set(x)==find_set(y)) tree = false;
union_set(x,y);
}
for(i=1;i<100;i++){
if(ranks[i]&& find_set(i)!= find_set(temp)){
tree= false;
}
}
if(tree) printf("Case %d is a tree.\n", size ++);
else printf("Case %d is not a tree.\n", size ++);
}
return 0;
}