解题思考(并查集)畅通工程

并查集基本思想框架,使用数组bin[n]对于每一个地点进行标记性质编号,如果ABC三个城市可被串联,则他们的bin[A,B,C]都相同;

两个函数findx与merge:findx向上层层找出bin[],merge合并:将二者得bin[]改为相同。

findx函数:

int	findx(int	x)
{
	int	r=x;
	while(bin[r]!=r)
		r=bin[r];
	return	r;
}

merge函数:

void	merge(int	x,int	y)
{
	int	fx,fy;
	fx=findx(x);
	fy=findx(y);
	if(fx!=fy)
		bin[fx]=fy;
}

对于不同题型只需更改主函数中具体设计即可,皆引用上述两函数。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

题型一:问再连接多少条畅通

M. 畅通工程

题目描述

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

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

Output
对每个测试用例,在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<bits/stdc++.h>
using	namespace	std;
int	bin[1003];
int	findx(int	x);
void	merge(int	x,int	y);
int	main()
{
	int	n,m;
	
	while(scanf("%d",&n),n)
	{
		scanf("%d",&m);
		int	x,y;
		int	count=-1;
		for(int	i=1;i<=n;i++)
			bin[i]=i;
		for(int	i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			merge(x,y);
		}
		for(int	i=1;i<=n;i++)
			if(bin[i]==i)
				count++;
		cout<<count<<endl;
		memset(bin,0,sizeof(bin));
	}
}
int	findx(int	x)
{
	int	r=x;
	while(bin[r]!=r)
		r=bin[r];
	return	r;
}
void	merge(int	x,int	y)
{
	int	fx,fy;
	fx=findx(x);
	fy=findx(y);
	if(fx!=fy)
		bin[fx]=fy;
}

题型二:能接通且路程最短

N. 还是畅通工程 

题目描述

Problem Description
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。

Output
对每个测试用例,在1行里输出最小的公路总长度。

输入样例

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

输出样例

3
5

思考:利用结构体,构建路的struct,元素为起点a,终点b,长度l,先对路按照长度由短到长排序,然后进行串联 。

#include<bits/stdc++.h>
using	namespace	std;
struct	road{
	int	a;
	int	b;
	int	l;
}arr[5000];
bool	cmp(road	x,road	y)
{
	return	x.l<y.l;
}
int	bin[5000];
int		findx(int	x);
void	merge(int	x,int	y);
int	main()
{
	int	n;
	while(cin>>n)
	{
		if(n==0)
			break;
		int	n1=n*(n-1)/2;
		int	count=0;
		for(int	i=0;i<n1;i++)
			cin>>arr[i].a>>arr[i].b>>arr[i].l;
		sort(arr,arr+n1,cmp);
		for(int	i=1;i<=n1;i++)
			bin[i]=i;
		for(int	i=0;i<n1;i++)
		{
			if(findx(arr[i].a)!=findx(arr[i].b))
			{
				merge(arr[i].a,arr[i].b);
				count=count+arr[i].l;
				//cout<<"作用"<<endl;	
			}	
		}
		cout<<count<<endl;	
	}
	return	0;
}
int		findx(int	x)
{
	int	r=x;
	while(bin[r]!=r)
		r=bin[r];
	return	r;
}
void	merge(int	x,int	y)
{
	int	fx,fy;
	fx=findx(x);
	fy=findx(y);
	if(fx!=fy)
		bin[fx]=fy;
}

题型三:添加限制条件,联通且都为单向路(不能出现环形道路)

Q. 小希的迷宫

题目描述

Problem Description
上次Gardon的迷宫城堡小希玩了很久,现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。

Input
输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。
整个文件以两个-1结尾。

Output
对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出”Yes”,否则输出”No”。

输入样例

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

输出样例

Yes
Yes
No

详解注释代码:

#include<bits/stdc++.h>
using	namespace	std;
struct	road{
	int	a;
	int	b;
}arr[100000];
//寻找最大值 
int		findmax(int	a,int	b)
{
	if(a>=b)
		return	a;
	else	
		return	b;
}
bool	cmp(road	x,road	y)
{
	return	findmax(x.a,x.b)>findmax(y.a,y.b);
}
//定义函数与全局变量 
int	bin[100000];
int	notice=0;
int	findx(int	x);
void	merge(int	x,int	y);
int	main()
{
	while(1)
	{
		int	count=0;
		int	max=0;
		notice=0;
		//输入道路信息 
		for(int	i=0;;i++)
		{
			cin>>arr[i].a>>arr[i].b;
			if(arr[i].a==0&&arr[i].b==0)
				break;
			if(arr[i].a==-1&&arr[i].b==-1)
				return	0;
			count++;
		}
		//寻找最大值 
		sort(arr,arr+count,cmp);
		max=findmax(arr[0].a,arr[0].b);
		//cout<<"max="<<max<<endl; 
		//初始化对应关系 
		for(int	i=1;i<=max;i++)
			bin[i]=i;
		//联通道路 (同时判断要联通的道路两侧是否已经联通,如已经联通的话,报错notice++)
		for(int	i=0;i<count;i++)
			merge(arr[i].a,arr[i].b);
		//判断是否出现多条分支情况
		int	compare[max];
		int	num=0;
		/*for(int	i=1;i<=max;i++)
			cout<<"findx(i)"<<findx(i)<<endl;*/
		int	m=0;
		for(int	i=1;i<=max;i++)
		{
			if(findx(i)==i)
			{
				m++;
				continue;
			}
			else
			{
				compare[i-m]=findx(i);
				num++;
			}
			
		}
		/*for(int	i=1;i<=num;i++)
			cout<<"compare[i]="<<compare[i]<<endl;*/
		for(int	i=1;i<num;i++)
		{
			int	temp;
			if(compare[i]!=compare[i+1])
			{
				notice++;
				//cout<<"notice ="<<notice<<endl; 
			}
		}
		//做出判断 
		if(notice)
			cout<<"No"<<endl;
		else
			cout<<"Yes"<<endl;
		//cout<<"notice ="<<notice<<endl;
		//重置对应关系 
		memset(bin,0,sizeof(bin));
	}
}
int	findx(int	x)
{
	int	r=x;
	while(bin[r]!=r)
		r=bin[r];
	return	r;
}
void	merge(int	x,int	y)
{
	int	fx,fy;
	fx=findx(x);
	fy=findx(y);
	//cout<<fx<<" "<<fy<<endl;
	if(fx!=fy)
		bin[fx]=fy;
	else	if(fx==fy)
		notice++;
	//cout<<"notice ="<<notice<<endl;
}
//?Riddler

 

 掌握了两个函数findx与merge,最简单的并查集问题也就迎刃而解了

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值