并查集优化方式:
1.按秩合并;
2.路径压缩;
按秩合并里的秩指的是一个父亲下属的节点数,也就是它的最大深度.
根据算法导论这两个优化只使用一个时间复杂度是nlogn级别,但同时使用时间复杂度将降为超线性O(mα(n)).α(n)是阿克曼函数的逆函数.
做了几道题,合并在这里:
小希的迷宫
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 18763 Accepted Submission(s): 5709
Problem Description
上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。
Input
输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。
整个文件以两个-1结尾。
Output
对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。
Sample Input
6 8 5 3 5 2 6 4
5 6 0 0
8 1 7 3 6 2 8 9 7 5
7 4 7 8 7 6 0 0
3 8 6 8 6 4
5 3 5 6 5 2 0 0
-1 -1
Sample Output
Yes
Yes
No
Author
Gardon
Source
HDU 2006-4 Programming Contest
Recommend
lxj
#include<stdio.h>
int father[100025]; bool sign[100025]; bool flag; int getfather(int v) { if (father[v]==v) return father[v]; father[v]=getfather(father[v]); return father[v]; } void merge(int u,int v) { int x=getfather(u); int y=getfather(v); if (x==y) flag=false; else father[x]=y; } int main() { int u,v; while (scanf("%d%d",&u,&v)!=EOF) { int i; if (u+v==-2) return 0; if (u+v==0) { printf("Yes\n"); continue; } flag=true; for (i=1;i<=100000;i++) { father[i]=i; sign[i]=false; } sign[u]=sign[v]=1; merge(u,v); while (scanf("%d%d",&u,&v)!=EOF) { if (u+v==0) break; if (flag) merge(u,v); sign[u]=sign[v]=true; } bool tot=0; for (i=1;i<=100000;i++) if (sign[i] && father[i]==i) tot++; if (tot!=1) flag=false; if (flag) printf("Yes\n"); else printf("No\n"); } }
More is better
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 327680/102400 K (Java/Others)
Total Submission(s): 9587 Accepted Submission(s): 3528
Problem Description
Mr Wang wants some boys to help him with a project. Because the project is rather complex, the more boys come, the better it will be. Of course there are certain requirements.
Mr Wang selected a room big enough to hold the boys. The boy who are not been chosen has to leave the room immediately. There are 10000000 boys in the room numbered from 1 to 10000000 at the very beginning. After Mr Wang's selection any two of them who are still in this room should be friends (direct or indirect), or there is only one boy left. Given all the direct friend-pairs, you should decide the best way.
Input
The first line of the input contains an integer n (0 ≤ n ≤ 100 000) - the number of direct friend-pairs. The following n lines each contains a pair of numbers A and B separated by a single space that suggests A and B are direct friends. (A ≠ B, 1 ≤ A, B ≤ 10000000)
Output
The output in one line contains exactly one integer equals to the maximum number of boys Mr Wang may keep.
Sample Input
4
1 2
3 4
5 6
1 6
4
1 2
3 4
5 6
7 8
Sample Output
4
2
Hint
A and B are friends(direct or indirect), B and C are friends(direct or indirect),
then A and C are also friends(indirect).
In the first sample {1,2,5,6} is the result.
In the second sample {1,2},{3,4},{5,6},{7,8} are four kinds of answers.
Author
lxlcrystal@TJU
Source
HDU 2007 Programming Contest - Final
Recommend
lcy
#include<stdio.h>
int Max; int father[10000025],rank[10000025]; int getfather(int v) { if (father[v]==v) return v; father[v]=getfather(father[v]); return father[v]; } void merge(int u,int v) { int x=getfather(u); int y=getfather(v); if (x!=y) { father[x]=y; rank[y]+=rank[x]; if (rank[y]>Max) Max=rank[y]; } } int main() { int n; while (scanf("%d",&n)!=EOF) { int i; for (i=1;i<=10000000;i++) { father[i]=i; rank[i]=1; } Max=1; for (i=1;i<=n;i++) { int u,int v; scanf("%d%d",&u,&v); merge(u,v); } printf("%d\n",Max); } return 0; }
Virtual Friends
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2563 Accepted Submission(s): 725
Problem Description
These days, you can do all sorts of things online. For example, you can use various websites to make virtual friends. For some people, growing their social network (their friends, their friends' friends, their friends' friends' friends, and so on), has become an addictive hobby. Just as some people collect stamps, other people collect virtual friends.
Your task is to observe the interactions on such a website and keep track of the size of each person's network.
Assume that every friendship is mutual. If Fred is Barney's friend, then Barney is also Fred's friend.
Input
Input file contains multiple test cases.
The first line of each case indicates the number of test friendship nest.
each friendship nest begins with a line containing an integer F, the number of friendships formed in this frindship nest, which is no more than 100 000. Each of the following F lines contains the names of two people who have just become friends, separated by a space. A name is a string of 1 to 20 letters (uppercase or lowercase).
Output
Whenever a friendship is formed, print a line containing one integer, the number of people in the social network of the two people who have just become friends.
Sample Input
1
3
Fred Barney
Barney Betty
Betty Wilma
Sample Output
2
3
4
Source
University of Waterloo Local Contest 2008.09
Recommend
chenrui
#include<stdio.h> #include<string.h> #include<stdlib.h>
struct A { int pre; int total; } E[100025]; int k; struct dictree { struct dictree *child[52]; int flag; }; struct dictree *root; void insert(char *str) { struct dictree *now,*next; int i,j,len,tmp; now=root; len=strlen(str); for (i=0;i<len;i++) { tmp=str[i]-'A'; if (now->child[tmp]) now=now->child[tmp]; else { next=(struct dictree *)malloc(sizeof(struct dictree)); for (j=0;j<52;j++) next->child[j]=0; next->flag=0; now->child[tmp]=next; now=next; } } now->flag=k; E[k].pre=k; E[k].total=1; k++; } int f_tree(char *str) { struct dictree *next; int i,len,tmp; len=strlen(str); next=root; for (i=0;i<len;i++) { tmp=str[i]-'A'; if (next->child[tmp]) next=next->child[tmp]; else return 0; } return next->flag; } int f_u(int k) { if (E[k].pre==k) return k; E[k].pre=f_u(E[k].pre); return E[k].pre; } void Union(int f1,int f2) { E[f1].pre=f2; E[f2].total+=E[f1].total; } int main() { int T,n,i,f1,f2,a,b; char str1[25],str2[25]; while (scanf("%d",&T)!=EOF) { while (T--) { scanf("%d",&n); root=(struct dictree *)malloc(sizeof(dictree)); for (i=0;i<52;i++) root->child[i]=0; root->flag=0; k=1; while (n--) { scanf("%s%s",&str1,&str2); for (i=0;str1[i];i++) if (str1[i]>='a') str1[i]-=6; for (i=0;str2[i];i++) if (str2[i]>='a') str2[i]-=6; a=f_tree(str1); b=f_tree(str2); if (a==0) {a=k;insert(str1);} if (b==0) {b=k;insert(str2);} f1=f_u(a); f2=f_u(b); if (f1!=f2) Union(f1,f2); printf("%d\n",E[f2].total); } } } return 0; }
Farm Irrigation
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3940 Accepted Submission(s): 1724
Problem Description
Benny has a spacious farm land to irrigate. The farm land is a rectangle, and is divided into a lot of samll squares. Water pipes are placed in these squares. Different square has a different type of pipe. There are 11 types of pipes, which is marked from A to K, as Figure 1 shows.
Figure 1
Benny has a map of his farm, which is an array of marks denoting the distribution of water pipes over the whole farm. For example, if he has a map
ADC
FJK
IHE
then the water pipes are distributed like
Figure 2
Several wellsprings are found in the center of some squares, so water can flow along the pipes from one square to another. If water flow crosses one square, the whole farm land in this square is irrigated and will have a good harvest in autumn.
Now Benny wants to know at least how many wellsprings should be found to have the whole farm land irrigated. Can you help him?
Note: In the above example, at least 3 wellsprings are needed, as those red points in Figure 2 show.
Input
There are several test cases! In each test case, the first line contains 2 integers M and N, then M lines follow. In each of these lines, there are N characters, in the range of 'A' to 'K', denoting the type of water pipe over the corresponding square. A negative M or N denotes the end of input, else you can assume 1 <= M, N <= 50.
Output
For each test case, output in one line the least number of wellsprings needed.
Sample Input
2 2
DK
HF
3 3
ADC
FJK
IHE
-1 -1
Sample Output
2
3
Author
ZHENG, Lu
Source
Zhejiang University Local Contest 2005
Recommend
Ignatius.L
#include<stdio.h> #include<string.h>
char s[60][60]; int f[3000][5],father[3000]; int getfather(int v) { if (father[v]==v) return v; father[v]=getfather(father[v]); return father[v]; } void merge(int u,int v) { int x=getfather(u); int y=getfather(v); father[x]=y; } int main() { int n,m; while (scanf("%d%d",&n,&m)!=EOF) { if (n+m==-2) return 0; int i,j; for (i=1;i<=n;i++) scanf("%s",s[i]); for (i=1;i<=n;i++) for (j=m;j>=1;j--) s[i][j]=s[i][j-1]; memset(f,0,sizeof(f)); for (i=1;i<=n;i++) for (j=1;j<=m;j++) { if (s[i][j]=='A') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][4]=true; } if (s[i][j]=='B') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][2]=true; } if (s[i][j]=='C') { f[(i-1)*m+j][3]=true; f[(i-1)*m+j][4]=true; } if (s[i][j]=='D') { f[(i-1)*m+j][2]=true; f[(i-1)*m+j][3]=true; } if (s[i][j]=='E') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][3]=true; } if (s[i][j]=='F') { f[(i-1)*m+j][2]=true; f[(i-1)*m+j][4]=true; } if (s[i][j]=='G') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][2]=true; f[(i-1)*m+j][4]=true; } if (s[i][j]=='H') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][3]=true; f[(i-1)*m+j][4]=true; } if (s[i][j]=='I') { f[(i-1)*m+j][2]=true; f[(i-1)*m+j][3]=true; f[(i-1)*m+j][4]=true; } if (s[i][j]=='J') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][2]=true; f[(i-1)*m+j][3]=true; } if (s[i][j]=='K') { f[(i-1)*m+j][1]=true; f[(i-1)*m+j][2]=true; f[(i-1)*m+j][3]=true; f[(i-1)*m+j][4]=true; } } for (i=1;i<=n*m;i++) father[i]=i; for (i=1;i<=n;i++) for (j=1;j<=m;j++) { int u=(i-1)*m+j; if (u>m && f[u][1] && f[u-m][3]) merge(u,u-m); if (j<m && f[u][2] && f[u+1][4]) merge(u,u+1); } int tot=0; for (i=1;i<=n*m;i++) if (father[i]==i) tot++; printf("%d\n",tot); } return 0; }
How Many Tables
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9025 Accepted Submission(s): 4407
Problem Description
Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.
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.
Input
The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.
Output
For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.
Sample Input
2
5 3
1 2
2 3
4 5
5 1
2 5
Sample Output
2
4
Author
Ignatius.L
Source
杭电ACM省赛集训队选拔赛之热身赛
Recommend
Eddy
#include<stdio.h> #include<string.h>
int f[1024][1024],father[1024]; int getfather(int v) { if (father[v]==v) return v; father[v]=getfather(father[v]); return father[v]; } void merge(int u,int v) { int x=getfather(u); int y=getfather(v); father[x]=y; } bool judge(int u,int v) { int x=getfather(u); int y=getfather(v); if (x==y) return true; else return false; } int main() { int n,m,T; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); memset(f,0,sizeof(f)); int ans=n; for (int i=1;i<=n;i++) father[i]=i; for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); if (f[u][v]) continue; f[u][v]=1; f[v][u]=1; if (judge(u,v)) continue; merge(u,v); ans--; } printf("%d\n",ans); } return 0; }
畅通工程
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 21937 Accepted Submission(s): 11413
Problem Description
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
Sample Output
1
0
2
998
Hint
Hint
Huge input, scanf is recommended.
Source
浙大计算机研究生复试上机考试-2005年
Recommend
JGShining
#include<stdio.h> #include<string.h>
int f[1024][1024],father[1024]; int getfather(int v) { if (father[v]==v) return v; father[v]=getfather(father[v]); return father[v]; } void merge(int u,int v) { int x=getfather(u); int y=getfather(v); father[x]=y; } bool judge(int u,int v) { int x=getfather(u); int y=getfather(v); if (x==y) return true; else return false; } int main() { int n,m; while (scanf("%d",&n)!=EOF) { if (n==0) return 0; scanf("%d",&m); memset(f,0,sizeof(f)); int ans=n-1; for (int i=1;i<=n;i++) father[i]=i; for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); if (f[u][v]) continue; f[u][v]=1; f[v][u]=1; if (judge(u,v)) continue; merge(u,v); ans--; } printf("%d\n",ans); } return 0; }
做HDU3172时结合了字典树,于是也做了几道题如下:
What Are You Talking About
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 102400/204800 K (Java/Others)
Total Submission(s): 9702 Accepted Submission(s): 3061
Problem Description
Ignatius is so lucky that he met a Martian yesterday. But he didn't know the language the Martians use. The Martian gives him a history book of Mars and a dictionary when it leaves. Now Ignatius want to translate the history book into English. Can you help him?
Input
The problem has only one test case, the test case consists of two parts, the dictionary part and the book part. The dictionary part starts with a single line contains a string "START", this string should be ignored, then some lines follow, each line contains two strings, the first one is a word in English, the second one is the corresponding word in Martian's language. A line with a single string "END" indicates the end of the directory part, and this string should be ignored. The book part starts with a single line contains a string "START", this string should be ignored, then an article written in Martian's language. You should translate the article into English with the dictionary. If you find the word in the dictionary you should translate it and write the new word into your translation, if you can't find the word in the dictionary you do not have to translate it, and just copy the old word to your translation. Space(' '), tab('\t'), enter('\n') and all the punctuation should not be translated. A line with a single string "END" indicates the end of the book part, and that's also the end of the input. All the words are in the lowercase, and each word will contain at most 10 characters, and each line will contain at most 3000 characters.
Output
In this problem, you have to output the translation of the history book.
Sample Input
START
from fiwo
hello difh
mars riwosf
earth fnnvk
like fiiwj
END
START
difh, i'm fiwo riwosf.
i fiiwj fnnvk!
END
Sample Output
hello, i'm from mars.
i like earth!
Hint
Huge input, scanf is recommended.
Author
Ignatius.L
自己做的不知错哪里了,粘上大牛的代码: #include"stdio.h" #include"ctype.h" #include"string.h" #include"stdlib.h"
struct dictree { struct dictree *child[26]; char aim[15]; int flag; }; struct dictree *root; void insert(char *source,char *temp) { int i,j; int len; struct dictree *current,*newnode; len=strlen(source); current=root; for(i=0;i<len;i++) { if(current->child[source[i]-'a']!=0) current=current->child[source[i]-'a']; else { newnode=(struct dictree *)malloc(sizeof(struct dictree)); for(j=0;j<26;j++) newnode->child[j]=0; newnode->flag=0; current->child[source[i]-'a']=newnode; current=newnode; } } current->flag=1; strcpy(current->aim,temp); } int flag; char ans[15]; void find(char *source) { struct dictree *current; int i; int len; len=strlen(source); current=root; for(i=0;i<len;i++) { if(current->child[source[i]-'a']!=0) current=current->child[source[i]-'a']; else {flag=1;return ;} } if(current->flag==0) {flag=1;return;} strcpy(ans,current->aim); } int main() { char str1[15],str2[15]; char str[3033]; int i,j; int len; char temp[15]; root=(struct dictree *)malloc(sizeof(struct dictree)); for(j=0;j<26;j++) root->child[j]=0; root->flag=0; scanf("%s",str1); while(scanf("%s",str1),strcmp(str1,"END")!=0) { scanf("%s",str2); insert(str2,str1); } scanf("%s",str); getchar(); while(gets(str),strcmp(str,"END")!=0) { /*****/ len=strlen(str); j=0; for(i=0;i<len;i++) { if(islower(str[i])) temp[j++]=str[i]; else { if(j) { temp[j]=0; flag=0; find(temp); if(flag) printf("%s",temp); else printf("%s",ans); } if(str[i]) printf("%c",str[i]); j=0; } } /*****/ printf("\n"); } return 0; }
Hat’s Words
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5426 Accepted Submission(s): 2024
Problem Description
A hat’s word is a word in the dictionary that is the concatenation of exactly two other words in the dictionary.
You are to find all the hat’s words in a dictionary.
Input
Standard input consists of a number of lowercase words, one per line, in alphabetical order. There will be no more than 50,000 words.
Only one case.
Output
Your output should contain all the hat’s words, one per line, in alphabetical order.
Sample Input
a
ahat
hat
hatword
hziee
word
Sample Output
ahat
hatword
Author
戴帽子的
Recommend
Ignatius.L
#include<stdio.h> #include<string.h>
class tnode { public: bool flag; tnode *child[26]; tnode () { flag=false; memset(child,0,sizeof(child)); } }; tnode *root; char s[50025][125]; void insert(char *str) { tnode *now=root; int i,j,len=strlen(str),tmp; for (i=0;i<len;i++) { tmp=str[i]-'a'; if (now->child[tmp]) now=now->child[tmp]; else { now->child[tmp]=new tnode; now=now->child[tmp]; } } now->flag=true; } void copy(int l,int r,char *src,char *tar) { int i; if (l>r) return; for (i=0;i<r-l+1+1;i++) tar[i]=src[i+l]; tar[r-l+1]='\0'; } bool find(char *str) { tnode *next=root; int i,len=strlen(str),tmp; for (i=0;i<len;i++) { tmp=str[i]-'a'; if (next->child[tmp]) next=next->child[tmp]; else return false; } return next->flag; } int main() { int i,j,n=1; char str1[125],str2[125]; root=new tnode; freopen("input.txt","r",stdin); while (scanf("%s",s[n])!=EOF) { insert(s[n]); n++; } n--; for (i=1;i<=n;i++) { int len=strlen(s[i]); for (j=0;j<len-1;j++) { memset(str1,0,sizeof(str1)); copy(0,j,s[i],str1); memset(str2,0,sizeof(str2)); copy(j+1,len-1,s[i],str2); if (find(str1) && find(str2)) printf("%s\n",s[i]); } } return 0; }
统计难题
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others)
Total Submission(s): 13090 Accepted Submission(s): 5540
Problem Description
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
Sample Output
2
3
1
0
Author
Ignatius.L
Recommend
Ignatius.L
#include<stdio.h> #include<string.h>
class tnode { public: int num; tnode *child[26]; tnode () { num=0; memset(child,0,sizeof(child)); } }; tnode *root; void insert(char *str) { tnode *now=root; int i,len=strlen(str),tmp; for (i=0;i<len;i++) { tmp=str[i]-'a'; if (now->child[tmp]==NULL) now->child[tmp]=new tnode; now=now->child[tmp]; now->num++; } } int find(char *str) { tnode *now=root; int i,len=strlen(str),tmp; for (i=0;i<len;i++) { tmp=str[i]-'a'; if (now->child[tmp]==NULL) return 0; now=now->child[tmp]; } return now->num; } int main() { char temp[15]; root=new tnode; while(gets(temp),strcmp(temp,"")!=0) insert(temp); while(gets(temp)!=0) printf("%d\n",find(temp)); return 0; }