并查集模板题
#include
#include
#include
using namespace std;
const int N = 5e4 + 5;
int fa[N]; // 记录每个节点的父节点
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int n, m, q;
int x, y;
scanf("%d%d%d", &n, &m, &q);
init(n);
for (int i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
unite(x, y);
}
for (int i = 0; i < q; i++) {
scanf("%d%d", &x, &y);
if (find(x) == find(y)) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
查找有多少个连通块,道路数目即为连通块数目减一
#include
#include
#include
using namespace std;
const int N = 5e4 + 5;
int fa[N]; // 记录每个节点的父节点
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int t, n, m;
int x, y;
while (scanf("%d", &n) != EOF && n) {
scanf("%d", &m);
init(n);
for (int i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
unite(x, y);
}
int ans = -1;
for (int i = 1; i <= n; i++)
if (find(i) == i)
ans++;
printf("%d\n", ans);
}
return 0;
}
注意一点:将罪犯和敌人的敌人进行合并
用一个数组来记录罪犯x的第一个敌人,再有敌人的话,就直接将x的敌人们并在一起即可
如果最后发现两个仇人已经在一个房间里面,此时只能结束并输出答案。
#include
#include
#include
#include
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int fa[N], foe[N]; // 记录每个节点的父节点,敌人
int n, m;
struct crim{
int a, b, c;
bool operator < (const crim &y) const {
return c > y.c;
}
};
crim arr[N];
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
cin >> n >> m;
init(n);
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &arr[i].a, &arr[i].b, &arr[i].c);
}
sort(arr, arr + m);
int res = 0;
for (int i = 0; i < m; i++) {
// 如果他们已经在一个房间
if (find(arr[i].a) == find(arr[i].b)) {
res = arr[i].c;
break;
} else {
// 标记敌人
if (foe[arr[i].a] == 0) {
foe[arr[i].a] = arr[i].b;
} else {
unite(foe[arr[i].a], arr[i].b);
}
// 标记敌人
if (foe[arr[i].b] == 0) {
foe[arr[i].b] = arr[i].a;
} else {
unite(foe[arr[i].b], arr[i].a);
}
}
}
cout << res << endl;
return 0;
}
将一个人和他敌人的敌人进行合并
最后再判断有几个连通块即可
#include
#include
#include
#include
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int fa[N], foe[N]; // 记录每个节点的父节点,敌人
int n, m;
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
char opt;
int p, q;
cin >> n >> m;
init(n);
while (m--) {
cin >> opt >> p >> q;
if (opt == 'F') {
unite(p, q);
} else {
if (foe[p] == 0) {
foe[p] = q;
} else {
unite(foe[p], q);
}
if (foe[q] == 0) {
foe[q] = p;
} else {
unite(foe[q], p);
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
if (find(i) == i) {
res++;
}
}
cout << res << endl;
return 0;
}
很闹心的一道题,先对两个元素进行离散化,如果e=1,就进行合并,否则就记录下来,最后判断两个元素是否在一个连通块内,同一个连通块内的数字一定相等。
#include
#include
#include
#include
using namespace std;
const int N = 1e6 + 5;
int fa[N]; // 记录每个节点的父节点
int xx[N], yy[N];
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int t, n;
int a, b, e;
int idx1, idx2, x, y;
cin >> t;
while (t--) {
scanf("%d", &n);
init(N - 5);
idx1 = idx2 = 1;
map<int, int> mp;
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &a, &b, &e);
if (mp[a] == 0) {
x = mp[a] = idx1;
idx1++;
} else {
x = mp[a];
}
if (mp[b] == 0) {
y = mp[b] = idx1;
idx1++;
} else {
y = mp[b];
}
if (e == 1) {
unite(x, y);
} else {
xx[idx2] = x, yy[idx2] = y;
idx2++;
}
}
bool flag = 1;
for (int i = 1; i < idx2; i++) {
//cout << find(xx[i]) << " " << find(yy[i]) << endl;
if (find(xx[i]) == find(yy[i])) {
flag = 0;
break;
}
}
cout << (flag ? "YES" : "NO") << endl;
}
return 0;
}
一开始用scanf超时了,改成了快速读入就过了
#include
#include
#include
#include
using namespace std;
const int N = 5e4 + 5;
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
} // 这是能判负数的C++快读模板
inline void write(int x)
{
if (x < 0) x = ~x + 1, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
int a[N];
int main(void)
{
int t, n;
int idx, tt;
t = read();
while (t--) {
map<int, bool> mp;
idx = 0;
n = read();
while (n--) {
tt = read();
if (mp[tt] == 0) {
mp[tt] = 1;
a[idx++] = tt;
}
}
for (int i = 0; i < idx; i++) {
write(a[i]);
putchar(' ');
}
puts("");
}
return 0;
}
离散化+并查集,需要将字符串与下标进行互换,用map来将字符串转换成下标,用数组或map来将下标转换成字符串
#include
#include
#include
#include
using namespace std;
const int N = 5e4 + 5;
typedef long long ll;
int fa[N], foe[N]; // 记录每个节点的父节点,敌人
int n, m;
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int idx = 1;
char op;
string s, t;
string arr[N];
map<string, int> mp;
init(N - 5);
while (cin >> op && op != '$') {
if (op == '#') {
cin >> s;
if (mp[s] == 0) {
mp[s] = idx;
arr[idx++] = s;
}
} else if (op == '+') {
cin >> t;
if (mp[t] == 0) {
mp[t] = idx;
arr[idx++] = t;
}
fa[mp[t]] = mp[s];
} else {
cin >> s;
t = arr[find(mp[s])];
cout << s << " " << t << endl;
}
}
return 0;
}