天是伊格那丢的生日。他邀请了很多朋友。现在该吃晚饭了。伊格那丢想知道他至少需要多少张桌子。你要注意,并不是所有的朋友都认识彼此,所有的朋友都不想和陌
生人呆在一起。
这个问题的一个重要规则是如果我告诉你A认识B, B认识C,这意味着A B C彼此认识,所以它们可以留在一个表中。
例如,如果我告诉你A知道B, B知道C, D知道E,那么A, B, C可以留在一个表中,而D, E必须留在另一个表中。所以伊格那丢至少需要两张桌子。输入以整数T(1<=T<=25)开始,表示测试用例的数量。
然后是T测试用例。
每个测试用例都以两个整数N和M开始(1<=N,M<=1000)。
N表示朋友的数量,从1到N,后面有M行。
每一行由两个整数A和B组成(A!=B),表示朋友A和朋友B认识。
两种情况之间将有一条空行。对于每个测试用例,只输出Ignatius至少需要多少个桌子。不要打印任何空白。
input
2
5 3
1 2
2 3
4 5
father[1~5]:1->2 2->3 3->3 4->5 5->5根节点只有2个
5 1
2 5
output
2
4
错误思路
开个father[N],默认为0,再写个初始化函数,每个案例先变0,再初始化成i,路径压缩,最后在1~n查询有多少个不同的根节点
错因: 没有理解合并并查集的操作
正确思路:合并并查集后再查询
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int N=1001; int father[N]; void inf(int n)//初始化 { for(int i=1;i<=N;i++) father[i]=i; } bool judge(int x)//判断是否为根节点 { if(x==father[x]) return true; else return false; } int findFather(int x) { //由于x在下面的while中会变成根结点, 因此先把原先的x保存一下 int temp=x; while(x!=father[x]){//寻找根节点 x=father[x]; } //到这里, x存放的是根结点。 下面把路径上的所有结点的father都改成根结点 while(temp!=father[temp]) { int t=temp;//因为temp要被father[temp]覆盖, 所以先保存temp的值, 以修改father[temp] temp=father[temp]; father[t]=x;//将原先的结点temp的父亲改为根结点x } return x; //返回根结点 } void Union (int a,int b)//U务必大写,因为union是个关键字 { int faA=findFather(a); int faB=findFather(b); if(faA==faB) return;//判断它们是否属于同一集合 else{ father[faB]=faA;//把其中一个的父亲结点指向另一个结点 } } int cnt(int n) { int ans=0; for(int i=1;i<=n;i++) if(judge(i)) ans++; return ans; } int main(){ int T;cin>>T; while(T--) { memset(father,0,sizeof(father)); int n,m; cin>>n>>m; inf(n); while(m--) { int a,b; cin>>a>>b; Union(a,b); } cout<<cnt(n)<<endl; } return 0; }