洛谷题单 115【数据结构1-3】集合(并查集部分)

文章目录

  • P1551 亲戚
  • P1536 村村通
  • P1525 关押罪犯
  • P1892 [BOI2003]团伙
  • P1955 [NOI2015]程序自动分析
  • P4305 [JLOI2011]不重复数字
  • P2814 家谱

P1551 亲戚

并查集模板题

#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;
}

P1536 村村通

查找有多少个连通块,道路数目即为连通块数目减一

#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;
}

P1525 关押罪犯

注意一点:将罪犯和敌人的敌人进行合并

用一个数组来记录罪犯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;
} 

P1892 [BOI2003]团伙

将一个人和他敌人的敌人进行合并

最后再判断有几个连通块即可

#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;
} 

P1955 [NOI2015]程序自动分析

很闹心的一道题,先对两个元素进行离散化,如果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;
}

P4305 [JLOI2011]不重复数字

一开始用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;
}

P2814 家谱

离散化+并查集,需要将字符串与下标进行互换,用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;
} 

你可能感兴趣的:(#,洛谷题单)