这道题说来和我颇有故事,最早是在一个月前的吉首大学同步赛遇到的,可见我博客 传送门。
这是我当时写的博客的截图= =我那天晚上和学长吃饭时也被学长说服了,认为并查集写不了… … 然后今天下午的牛客上拉出了重现赛,我看了一下这道题,突然觉得可以用并查集写。事实证明我的感觉是对的
先上原题:
链接:传送门
武林内纷乱不断,各地都自立门派,门派的人为了识别门内弟子,给了每个人一块令牌,这个令牌有个神奇的地方,
门派内的弟子的令牌不一定相同,
下面是他的识别规则:
每个人的令牌都是一串数字,如果两个人的令牌有相似的地方,即有相同的数字,那就属于同一个门派,特别的,
如果两个人没有相同的数字,但是这个两个人都和另一个人有相同的数字,那么这三个人同属一个门派,现在有一个任务
,给你n个令牌,让你认出有多少个门派
例如
3
13579
2468
12
这里答案应该是1,因为第一个人和第二个人同时和第三个人有关系
输入描述:
第一行 输入一个t,代表数据组数(1<=t<=10)
第二行 输入一个n,代表n块令牌 (1<=n<=1000)
下面n行,每行一个数字序列,(1<=len<=1000)
输出描述:
有t行,每行一个数,代表有这组数据有多少个门派
示例1
输入
2
3
13579
2468
12
5
12
23
34
5
5678
输出
1
2
并查集解题思路: 先看题意,很简单,即有相同数字的是一个门派,也就是可以建立集合关系。然后我开了一个二维数组tong[i][j]
来记录第i
个人包含0~9中的哪些数字,然后就是暴力建立关系、找关系了。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <cmath>
#include <algorithm>
using namespace std;
#define ll long long
char p[1005];
int pre[1005];
int t[1005];
int tong[1005][15];
int find(int x)//查找根结点
{
int r = x;
while (r != pre[r])
r = pre[r];
int k = x, temp;
while (pre[k] != r)//压缩路径
{
temp = pre[k];
pre[k] = r;
k = temp;
}
return r;
}
void mix(int x, int y)//建立x、y关系
{
int fx = find(x);
int fy = find(y);
if (fx != fy)
pre[fx] = fy;
return;
}
int main()
{
int time;
cin >> time;
while (time--)
{
memset(tong, 0, sizeof(tong));
memset(t, 0, sizeof(t));
memset(pre, 0, sizeof(pre));
int n;
cin >> n;
for (int i = 1; i <= n; i++)
pre[i] = i;
for (int i = 1; i <= n; i++)
{
memset(p, '\0', sizeof(p));
cin >> p;
for (int j = 0; j < strlen(p); j++)
tong[i][p[j] - '0'] = 1;
}
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
for (int l1 = 0; l1 <= 9; l1++)
if (tong[i][l1] == tong[j][l1] && tong[i][l1])
mix(i, j);
int ans = 0;
for (int i = 1; i <= n; i++)
t[find(i)] = 1;
for (int i = 1; i <= n; i++)
if (t[i])
ans++;
cout << ans << endl;
}
return 0;
}