poj 1308 Is It A Tree?
http://poj.org/problem?id=1308
满足一下条件是树:
(1)只有一个根;
(2)一个入度为0的节点,其他节点入度都为1
第一个用并查集,第二个搞个数组统计下就OK。
我用set来存点,因为这些点在输入中可能出现多次,用set只会存一次,set方便些啦!
#include <cstdio>
#include <cstdlib>
#include <set>
#include <iterator>
#define N 10001
using namespace std;
set<int> s;
set<int>::iterator iter;
int father[N];
int degree[N];
void make_set();
int find_set(int x);
void unin(int x,int y);
int main()
{
int i,j,f,a,b,k,zero;
bool fail;
k = 1 ;
while(scanf("%d%d",&a,&b) && (a!= -1 && b != -1))
{
/*
空树也是树
*/
if( a == 0 && b == 0)
{
printf("Case %d is a tree.\n",k++);
continue;
}
/*
每组数据都得清空 set容器
*/
s.clear();
s.insert(a);
s.insert(b);
/*
初始化
*/
make_set();
unin(a,b);
++degree[b];
while(scanf("%d%d",&a,&b) && (a&&b))
{
s.insert(a);
s.insert(b);
unin(a,b);
++degree[b];
}
f = find_set(father[*s.begin()]);
zero = 0 ;//统计有几个入度为 0 的点
fail = false;
for( iter = s.begin(); iter != s.end(); ++iter)
{
/*
树的每个点的祖先都一样,否则不是树
*/
if(f != find_set(father[*iter]))
{
fail = true;
break;
}
/*
树不能有入度大于 1的节点,否则不是树
*/
if(degree[*iter]>1)
{
fail = true;
break;
}
else
{
/*
统计根
*/
if(degree[*iter] == 0)
++zero;
}
}
printf("Case %d ",k++);
if(fail || zero != 1)
{
printf("is not a tree.\n");
}
else
printf("is a tree.\n");
}
return 0;
}
void make_set()
{
int i;
for(i= 1; i < N; ++i)
{
father[i] = i;
degree[i] = 0;
}
}
int find_set(int x)
{
int t;
if(x != father[x])
father[x] = find_set(father[x]);//路径压缩
return father[x];
}
/*
因为是树,合并是有方向的
*/
void unin(int x,int y)
{
x = find_set(x);
y = find_set(y);
father[y] = x;
}
poj 1611 The Suspects
题目描述:
有很多组学生,在同一个组的学生经常会接触,也会有新的同学的加入。但是SARS是很容易传染的,只要在改组有一位同学感染SARS,那么该组的所有同学都被认为得了SARS。现在的任务是计算出有多少位学生感染SARS了。假定编号为0的同学是得了SARS的。
将每一组第一个同学和每一个同学合并就可以了。
采用num[]存储该集合中元素个数,并在集合合并时更新num[]即可。然后找出0所在的集合的根节点x,因此,num[x]就是answer了。
#include<cstdio>
using namespace std;
const int MAXN=30001;
int father[MAXN];
int num[MAXN];
void make_set(int x){
father[x]=x;
num[x]=1;
}
int find_set(int x){
if(x!=father[x])
father[x]=find_set(father[x]);
return father[x];
}
void union_set(int x,int y){
x=find_set(x);
y=find_set(y);
if(x==y)
return ;
father[y]=x;
num[x]+=num[y];
}
int main(){
int n,m,t,x,y;
while(scanf("%d%d",&n,&m)){
if(m==0&&n==0)
break;
if(m==0){
printf("1\n");
continue;
}
for(int i=0;i<n;i++)
make_set(i);
for(int i=0;i<m;i++){
scanf("%d",&t);
scanf("%d",&x);
for(int j=1;j<t;j++){
scanf("%d",&y);
union_set(x,y);
}
}
x=find_set(0);
printf("%d\n",num[x]);
}
}
POJ 1703 Find them, Catch them
题意 :有两个不同的帮派,每个帮派至少有一个人。 判断两个人是否属于同一个帮派。
有 T 组测试数据。
给你 N 个人,编号从 1 到 N,操作 M 次。
每次操作输入一个字符和两个数 x ,y
如果字符为 A 则判断 x 和 y 是否属于同一个帮派,并且输出结果。
如果字符为 D 则明确告诉你 x 和 y 是属于不同帮派的。
详细分析:
#include<cstdio>
const int maxn = 100000+10;
int p[maxn]; //存父亲节点
int r[maxn]; //存与根节点的关系,0 代表同类, 1代表不同类
int find(int x) //找根节点
{
if(x == p[x]) return x;
int t = p[x]; //记录父亲节点 方便下面更新r[]
p[x] = find(p[x]);
r[x] = (r[x]+r[t])%2; //根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
return p[x]; //容易忘记
}
void Union(int x, int y)
{
int fx = find(x); //x所在集合的根节点
int fy = find(y);
p[fx] = fy; //合并
r[fx] = (r[x]+1+r[y])%2; //fx与x关系 + x与y的关系 + y与fy的关系 = fx与fy的关系
}
void set(int n)
{
for(int x = 1; x <= n; x++)
{
p[x] = x; //自己是自己的父节点
r[x] = 0; //自己和自己属于同一类
}
}
int main()
{
int T;
int n, m;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%*c", &n, &m);
set(n);
char c;
int x, y;
while(m--)
{
scanf("%c%d%d%*c", &c, &x, &y); //注意输入
//printf("%c\n", c);
if(c == 'A')
{
if(find(x) == find(y)) //如果根节点相同,则表示能判断关系
{
if(r[x] != r[y]) printf("In different gangs.\n");
else printf("In the same gang.\n");
}
else printf("Not sure yet.\n");
}
else if(c == 'D')
{
Union(x, y);
}
}
}
return 0;
}
404

被折叠的 条评论
为什么被折叠?



