★实验任务
小z为竞选他们村的村长,他承诺在这个村上建其地下公路来方便大家出行。
但这个地下公路有一个奇怪的规定,每条公路上只能使用有限种交通工具,从出 发点到终点只能使用一种交通工具。现在小 z想知道从某点到另一点可以采用
多少种交通工具。
为简化起见,为地下公路的 N个点编号,分别为 0~N-1,地下公路的交通 工具一共 C种,编号为 1~C。
★数据输入
输入第一行包括两个整数,N,M,分别表示有 N个点,M条公路。接下来 M行,每行三个数 u,v,c,表示从 u到 v可以采用 c种交通工具。接着输入 Q,表示询问个数,接下来 Q行,每行两个数 p,q,表示查询从 p到 q有多少
种交通工具方式。
★数据输出
对于 Q个询问,分别输出每个询问的结果。
解题思路:看到题目之后,根据题目所描述的总共最多十种交通工具,然后判断每两个点之间有几种交通工具可以达到,可以每种交通工具下面都做一个并查集进行查询看这种交通工具是否可以使得两点联通;
并查集实现核心及易错细节:我一般通过数组的方式实现并查集,每个点都有一个fa,在数据比较小的时候可以直接通过一个fa数组存储每一个点的父亲节点,然后就是查询根节点和合并两棵子树(集合);
查询过程:有直接每次递归查询,可根据题目需要,判断是否需要在递归过程中压缩路径(在只需要找到每个点的根节点的时候压缩路径肯定会减小查询时间的,但是如果还需要精确到每条路径怎么走的时候就不能压缩路径了)
int find_Fa(int tran, int roa)
{
if (fa[tran][roa] != roa) { fa[tran][roa] = find_Fa(tran, fa[tran][roa]); }
return fa[tran][roa]; //通过递归返回的方式将路径压缩
}
合并过程:在每次对两个节点判断做合并的时候可以通过对该节点的高度判断哪棵树合并到另一棵树下面
void uni(int tran,int r1,int r2)
{
r1 = find_Fa(tran, r1), r2 = find_Fa(tran, r2);
if (r1 == r2)return;
if (hei[tran][r1] > hei[tran][r2])fa[tran][r2] = r1;
else
{
fa[tran][r1] = r2;
if (hei[tran][r1] == hei[tran][r2])hei[tran][r2]++;
}
}
我出现的错误:在合并的时候直接用的是fa[r1]和fa[r2]没有对他们进行搜索根节点,导致整个过程一直出错;
此题代码:
#include<cstdio>
#include<cstdlib>
int fa[11][105];
int hei[11][105] = { 0 };
int find_Fa(int tran, int roa)
{
if (fa[tran][roa] != roa) { fa[tran][roa] = find_Fa(tran, fa[tran][roa]); }
return fa[tran][roa]; //通过递归返回的方式将路径压缩
}
void uni(int tran,int r1,int r2)
{
r1 = find_Fa(tran, r1), r2 = find_Fa(tran, r2);
if (r1 == r2)return;
if (hei[tran][r1] > hei[tran][r2])fa[tran][r2] = r1;
else
{
fa[tran][r1] = r2;
if (hei[tran][r1] == hei[tran][r2])hei[tran][r2]++;
}
}
int main()
{
int n, m, i, j, roa1, roa2, tran;
scanf("%d%d", &n, &m);
for (i = 1; i < 11; i++)
{
for (j = 0; j < n; j++)
{
fa[i][j] = j;
}
}
for (i = 0; i < m; i++)
{
scanf("%d%d%d", &roa1, &roa2, &tran);
uni(tran, roa1, roa2);
}
int que, sum = 0; scanf("%d", &que);
for (i = 0; i < que; i++)
{
sum = 0;
scanf("%d%d", &roa1, &roa2);
for (j = 1; j <= 10; j++)
{
if (find_Fa(j, roa1) == find_Fa(j, roa2))sum++;
}
printf("%d
", sum);
}
return 0;
}
并查集还需要多加练习;