这道题是到好题,要想做出这道题,得学会节点映射。。
因为题目中讲了,有合并,还有删除, 那删除一个节点后,其他的节点关系依旧,但是这个被删除的节点就得孤立, 而且被孤立之后可能重新回到某个集合中,那如何处理呢?
就是映射,比如1到n ,,,,,,现在要合并的是4 和 5 号节点,我们合并的并不是 4 和 5 ,而是 Union(mp[4],mp[5]),mp[i]就是节点i 的映射。。
最后判断有多少个不同特征的邮件就 看有少个结合,也就是看有多少个点事根节点。。
请看代码:
#include <iostream>
#define N 1001000
using namespace std;
int f[N],mp[N],cnt[N]; //这块数组得开这么大,不然wrong 尽管题目要求的是 10 的5次方,
int find(int x)//查
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
void make(int a,int b)//并
{
int f1=find(a);
int f2=find(b);
if(f1!=f2)
{
f[f1]=f2;
cnt[f2]+=cnt[f1];
cnt[f1]=0; //因为该点的父节点转移了,所以让他的cnt[]置0,为了后面的根节点个数的判断。。
}
}
int main()
{
int n,m,test=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)break;
test++;
for(int i=0;i<n;i++)
{
f[i]=i;
mp[i]=i; //初始化时,每个节点的映射先等于本身
cnt[i]=1;
}
char s[10];
int a,b;
for(int i=0;i<m;i++)
{
//getchar();
scanf("%s",s);
if(s[0]=='M')
{
scanf("%d%d",&a,&b);
make(mp[a],mp[b]);
}
else if(s[0]=='S')
{
scanf("%d",&a);
int root=find(mp[a]); //映射操作,每次并的,查的不是这个编号本身,而是这个编号的映射
cnt[root]--;
mp[a]=n++;
f[mp[a]]=mp[a];
cnt[mp[a]]=1;
}
}
int sum=0;
for(int i=0;i<n;i++)
if(cnt[i])sum++;
printf("Case #%d: %d\n",test,sum);
}
return 0;
}