并查集模板【畅通工程/More is Better】

1.初始化:所有的结点都是一个单独的分支,用Tree[]存储结点分支的根。

2.对于每一条边,递归寻找其所属于的分支的根节点。

(1)如果相同,说明它们属于同一个分支。

(2)如果不同,则通过这条边可以连通,所以将它们合并成一个分支(注意必须对根节点操作,思考为什么?)。

3.最后统计根节点数量。

 

应用:(1)图的分支数(畅通工程);(2)单一分支最多的结点数(More is Better)。

题目描述

    某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

输入描述:

    测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。 
    注意:两个城市之间可以有多条道路相通,也就是说
    3 3
    1 2
    1 2
    2 1
    这种输入也是合法的
    当N为0时,输入结束,该用例不被处理。

输出描述:

对每个测试用例,在1行里输出最少还需要建设的道路数目。

输入

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

输出

1
0
2
998
//畅通工程
#include
#include
using namespace std; 
int Tree[1001];
int findRoot(int x){
	if(Tree[x]==-1) return x;//没有双亲结点,本身就是根 
	else{
		int temp = findRoot(Tree[x]);
		Tree[x]=temp;
		return temp;
		//return findRoot(Tree[x]); 
		//否则递归找双亲结点,记录找到的根节点,使得查找的结点是其根节点的孩子 
	}
	 
}
int main(void)
{
	int n,m;
	while(cin>>n){//n个结点 
		if(n==0) break;
		cin>>m;//有m条边 
		for(int i=1;i<=n;i++){//初始化 
			Tree[i]=-1;
		}
		int a,b;
		for(int i=1;i<=m;i++){ 
			cin>>a>>b;
			int na = findRoot(a);
			int nb = findRoot(b);//查找两个结点的根 
			if(na!=nb){
				Tree[na]=nb; 
				//来自不同的分支,现在可以合并在一起,注意必须是根合并在一起 
			} 
		} 
		int ans=0;
		for(int i=1;i<=n;i++){//有多少个根节点 
			if(Tree[i]==-1) ans++;
		}
		cout<
//More is better 
#include
#include
#define N 1000001 
using namespace std; 
int Tree[N];
int size[N]; 
int findRoot(int x){
	if(Tree[x]==-1) return x;//没有双亲结点,本身就是根 
	else{
		int temp = findRoot(Tree[x]);
		Tree[x]=temp;
		return temp;
	} 
}
int main(void)
{
	int n,m;
	while(cin>>m){//n个结点 
		for(int i=1;i<=N;i++){ 
			Tree[i]=-1;
			size[i]=1;
		}
		int a,b;
		for(int i=1;i<=m;i++){ 
			cin>>a>>b;
			int na = findRoot(a);
			int nb = findRoot(b);
			if(na!=nb){
				Tree[na] = nb; 
				size[nb] += size[na];
			} 
		} 
		int num=1;
		for(int i=1;i<=N;i++){//有多少个根节点 
			if(Tree[i]==-1&&size[i]>num) num=size[i];
		}
		cout<

题目描述

该题的目的是要你统计图的连通分支数。

输入描述:

每个输入文件包含若干行,每行两个整数i,j,表示节点i和j之间存在一条边。

输出描述:

输出每个图的联通分支数。

输入

1 4
4 3
5 5

输出

2

链接:https://www.nowcoder.com/questionTerminal/7c29cdfa28274c86afc9e88c07448a10

注意点:题目没有说结点从1到N。这里可以采用set方式或者定义mark[]数组记录(如果下次又用字符呢)。

实践证明:set相当耗内存。

#include
using namespace std;
int const MAX = 1000005;
int Tree[MAX]; 
bool mark[MAX];
int findRoot(int x){
	if(Tree[x]==-1) return x;
	int tmp = findRoot(Tree[x]);
	Tree[x]=tmp;	
	return tmp;
}
int main(void) {
	int a,b;
	for(int i=1;i<=MAX;i++) {Tree[i]=-1;mark[i]=false;}
	while(cin>>a>>b){
		//if(a==0&&b==0) break;
		mark[a]=true;mark[b]=true;
		int a_root = findRoot(a);
		int b_root = findRoot(b);
		if(a_root!=b_root)	Tree[a_root]=b_root;
	}
	int count=0;
	for(int i=1;i<=MAX;i++){
		if(mark[i]==true&&Tree[i]==-1) count++;
	}
	cout<
#include 
#include 
using namespace std;
#define N 1000000
int Tree[N];
int findRoot(int x){
    if(Tree[x]==-1) return x;
    int temp=findRoot(Tree[x]);
    Tree[x]=temp;
    return temp;
}
int main(){
    int a,b;
    sets;
    for(int i=0;i>a>>b){
        s.insert(a);
        s.insert(b);
        a=findRoot(a);
        b=findRoot(b);
        if(a!=b) Tree[a]=b;
    }
    int ans=0;
    set::iterator it;
    for(it=s.begin ();it!=s.end ();it++)
        if(Tree[*it]==-1) ans++;
    cout<

 

你可能感兴趣的:(算法基础)