并查集基本思想框架,使用数组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,最简单的并查集问题也就迎刃而解了