十月末析题相关之并查集
对于第一次女生训练的析题相关:
首先是第一题:
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 }
对于并查集的总结先到这里,还需要大大的加深学习力度。

浙公网安备 33010602011771号