对于第一次女生训练的析题相关:
首先是第一题:
How Many Tables
One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.
For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #include <math.h> 6 7 using namespace std; 8 9 int pre[1001]; 10 struct node{ 11 int x; 12 int y; 13 }a[1001]; 14 15 int find(int x){ 16 int r=x; 17 while(pre[r]!=r) 18 r=pre[r]; 19 return r; 20 } 21 22 int main(){ 23 int N,M,i,num; 24 int T; 25 scanf("%d",&T); 26 while(T--){ 27 scanf("%d%d",&N,&M);//N表示朋友个数,M表示关系条数 28 num=N; //N个朋友最多坐n桌之间 29 for(i=1;i<=N;i++) //pre数组初始化 30 pre[i]=i; 31 for(i=1;i<=M;i++){ 32 scanf("%d%d",&a[i].x,&a[i].y); 33 //在这里连接认识的朋友们 34 int b=find(a[i].x); 35 int c=find(a[i].y); 36 if(b!=c){ 37 pre[c]=b; 38 num--; 39 } 40 } 41 printf("%d\n",num); //对于输出格式有变 42 } 43 return 0; 44 }
那么我决定首先去刷并查集的水题,学姐说可以先刷水题,再慢慢提高难度,要建立自己的信心。
以下为A的水题:
A – 畅通工程 HDU 1232(题意大致)
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,
1 #include<iostream> 2 #include<stdio.h> 3 4 int pre[1001]; 5 6 int find(int x){ //查找根节点 7 int r=x; 8 while(pre[r]!=r) //返回根节点 9 r=pre[r]; 10 int i=x,j; 11 while(i!=r){ //路径压缩 12 j=pre[i]; //在改变上级之前用临时变量j记录下它的值 13 pre[i]=r; //把自己的上级直接改为根节点,终极Boss 14 i=j; //接着去改变自己原本的上级它的上级为终极Boss 15 } 16 return r; 17 } 18 19 void join(int a,int b){ 20 //判断a与b是否连通 21 //如果已经连通,那么可以不用管 22 //如果不连通,就把它们所在的连通分支合并起来 23 int fx = find(a); 24 int fy = find(b); 25 if(fx!=fy) 26 pre[fx]=fy; //没有优化,产生的树可能是畸形树 27 } 28 29 int main(){ 30 int n; 31 while(scanf("%d",&n)&&n!=0){ 32 int m,i,a,b,ans; 33 for(i=1;i<=n;i++) //对pre数组的初始化 34 pre[i]=i; 35 //memset(pre,0,pre+n); 36 scanf("%d",&m); 37 for(i=1;i<=m;i++){ 38 scanf("%d%d",&a,&b); 39 join(a,b); 40 } 41 for(ans=0,i=1;i<=n;i++){ 42 if(pre[i]==i) 43 ans++; 44 } 45 ans-=1; 46 printf("%d\n",ans); 47 } 48 return 0; 49 }
再来是
B - 小希的迷宫 | Is it a tree ? HDU 1272 (大致题意)
上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。
Input
输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。
整个文件以两个-1结尾。
其实觉得这题一开始看会有些晕,就像训练里面那题,也需要看久一些,才能正确使用模板。看来我对并查集的理解还是不够。。。。
而且这道题错了好几次,输出的小错误在此就不需提了,是一些算法顺序上的错误,模板不够熟啊不够熟!
最终AC代码:
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 5 const int N=100010; 6 int pre[N],s; 7 int num[N],v[N]; 8 9 void Init(){ 10 for(int i=1;i<=N;i++){ 11 pre[i]=i; 12 v[i]=num[i]=1; //v[]记录状态起始为1 13 } 14 } 15 int find(int x){ //查找根节点 16 int r=x; 17 while(pre[r]!=r) //返回根节点 18 r=pre[r]; 19 /*int i=x,j; 20 while(i!=r){ //路径压缩 21 j=pre[i]; //在改变上级之前用临时变量j记录下它的值 22 pre[i]=r; //把自己的上级直接改为根节点,终极Boss 23 i=j; //接着去改变自己原本的上级它的上级为终极Boss 24 }*/ 25 return r; 26 } 27 28 int join(int a,int b){ 29 //判断a与b是否连通 30 int fx = find(a); 31 int fy = find(b); 32 if(fx==fy){ //此时如果连通那么就会使这棵树产生回路 33 return 1; 34 } 35 else{ 36 pre[fy]=fx; //当两个房间不属于同一棵树时连接 37 num[fx]+=num[fy]; 38 s=num[fx]; 39 return 0; 40 //没有优化,产生的树可能是畸形树 41 } 42 } 43 44 int main(){ 45 int i,a,b; 46 while(scanf("%d%d",&a,&b)&&(a!=-1||b!=-1)){ 47 Init(); 48 if(a==0&&b==0){ //判断空树情况 49 printf("Yes\n"); 50 continue; 51 } 52 int ok=join(a,b),n=v[a]+v[b]; //ok记录是否存在环,n记录节点总数 53 v[a]=v[b]=0; 54 for(i=2;i<=N;i++){ 55 scanf("%d%d",&a,&b); 56 if(a==0&&b==0) 57 break; 58 n+=(v[a]+v[b]); 59 v[a]=v[b]=0; 60 ok+=join(a,b); 61 } 62 if(ok) 63 printf("No\n"); 64 else{ 65 if(s==n) 66 printf("Yes\n"); 67 else 68 printf("No\n"); 69 } 70 } 71 return 0; 72 }
对于并查集的总结先到这里,还需要大大的加深学习力度。