题意:一个人过生日邀请了n个朋友来,不过他的朋友不习惯和陌生人在一张桌子上,问你最少要准备几张桌子。若a认识b,b认识c,a也可以和c坐一起。
基础并查集,模板
路径压缩优化、秩优化
链接:hdu 1213
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;
const int maxn = 1050 + 10;
const int inf = 1e9 + 7;
const int N = 1005;// 初始化并查集
int n;
int father[N];
void init() {
for(int i = 0; i < N; i++)
father[i] = i;
}
int getfather(int x) {
while(x != father[x])
x = father[x];
return x;
}
// 合并两个元素所在的集合
void unions(int x, int y) {
x = getfather(x);
y = getfather(y);
if(x != y)
father[x] = y;
}
// 判断两个元素是否属于同一个集合
bool same(int x, int y) {
return getfather(x) == getfather(y);
}
int x[maxn], y[maxn];
int r[maxn];
int d, cnt = 0;
bool dic(int a, int b) {
if((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]) <= d * d)
return 1;
return 0;
}
int main()
{
int T, kcase = 0;
cin >> T;
while(T--) {
int m;
cin >> n >> m;
init();
while(m--) {
int a, b;
cin >> a >> b;
unions(a, b);
}
m++;
for(int i = 1; i <= n; i++) {
if(father[i] == i) {
m++;
}
}
cout << m << endl;
}
return 0;
}
路径压缩
int getfather(int x) {
if(x != father[x])
father[x] = getfather(father[x]); // 路径压缩修改的是father数组
return father[x];
}
秩优化
void unite (int x, int y) {
x = getfather(x);
y = getfather(y);
if(x == y) return;
if(rnk[x] < rnk[y]) {
father[x] = y;
}
else {
father[y] = x;
if(rnk[x] == rnk[y]) rnk[x]++;
}
}
int getfather(int x) {
if(x != father[x])
father[x] = getfather(father[x]); // 路径压缩修改的是father数组
return father[x];
}
题意:给你N台电脑,从1-N。一个数字,表示两台计算机的最大通信距离,超过这个距离就无法进行通信。然后分别告诉这些电脑的坐标,接下来有两种操作,第一种O表示这点电脑修好,第二种S,表示测试这两台电脑能不能进行正常的通信
修好的遍历看能否通信
链接:poj 2236
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;
const int maxn = 1050 + 10;
const int inf = 1e9 + 7;
const int N = 1005;// 初始化并查集
int n;
int father[N];
void init() {
for(int i = 0; i < N; i++)
father[i] = i;
}
// 获取根结点 1
int getfather(int x) {
while(x != father[x])
x = father[x];
return x;
}
/*/ 获取根结点 2
int getfather(int x) {
if(x != father[x])
father[x] = getfather(father[x]); // 路径压缩修改的是father数组
return father[x];
}*/
// 合并两个元素所在的集合
void unions(int x, int y) {
x = getfather(x);
y = getfather(y);
if(x != y)
father[x] = y;
}
// 判断两个元素是否属于同一个集合
bool same(int x, int y) {
return getfather(x) == getfather(y);
}
int x[maxn], y[maxn];
int r[maxn];
int d, cnt = 0;
bool dic(int a, int b) {
if((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]) <= d * d)
return 1;
return 0;
}
int main()
{
scanf("%d %d", &n, &d);
for(int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
init();
char s[5];
int a, b;
while(scanf("%s", s) != EOF) {
if(s[0] == 'O') {
scanf("%d", &a);
r[++cnt] = a;
for(int i = 1; i < cnt; i++) {
if(dic(r[i], a)) {
unions(r[i], a);
}
}
}
else {
scanf("%d %d", &a, &b);
if(same(a, b)) {
puts("SUCCESS");
}
else {
puts("FAIL");
}
}
}
return 0;
}
题意:有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数。
对于每一组的孩子都和第一个孩子合并就好了
链接:poj 1611
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;
const int maxn = 30050 + 10;
const int inf = 1e9 + 7;
int n;
int father[maxn];
int a[maxn];
void init(int n) {
for(int i = 0; i <= n; i++)
father[i] = i;
}
int getf(int x) {
if(x != father[x])
father[x] = getf(father[x]); // 路径压缩修改的是father数组
return father[x];
}
// 合并两个元素所在的集合
void unions(int x, int y) {
x = getf(x);
y = getf(y);
if(x != y)
father[x] = y;
}
int main()
{
int m;
while(scanf("%d %d", &n, &m) && (n || m)) {
init(n);
int k;
while(m--) {
scanf("%d", &k);
for(int i = 0; i < k; i++) {
scanf("%d", &a[i]);
unions(a[i], a[0]);
}
}
int sum = 0;
for(int i = 0; i < n; i++) {
if(getf(i) == father[0]) {
sum++;
}
}
printf("%d\n", sum);
}
return 0;
}
题意:中文题
已经在同一集合的就不是了
链接:hdu 1272
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;
const int maxn = 200000 + 10;
const int inf = 1e9 + 7;
int n;
int father[maxn];
int r[maxn];
int a[maxn];
int cnt, flag;
void init() {
for(int i = 0; i <= maxn; i++) {
father[i] = i;
r[i] = 0;
a[i] = 0;
cnt = 0;
flag = 1;
}
}
// 获取根结点
int getf(int x) {
return x == father[x] ? x : father[x] = getf(father[x]);
}
// 合并两个元素所在的集合
bool unions(int x, int y) {
x = getf(x);
y = getf(y);
if(x == y) {
return false;
}
father[x] = y;
return true;
}
int main()
{
int T, kcase = 0;
int m;
init();
while(~scanf("%d %d", &n, &m)) {
if(n == -1 && m == -1) {
break;
}
else if(n == 0 && m == 0) {
int res = 0;
for(int i = 0; i < cnt; i++) {
if(father[a[i]] == a[i]) {
res++;
}
}
if(res >= 2) flag = 0;
if(flag) puts("Yes");
else puts("No");
init();
}
else {
if(r[n] == 0) {
r[n] = 1;
a[cnt++] = n;
}
if(r[m] == 0) {
r[m] = 1;
a[cnt++] = m;
}
if(!unions(n, m)) {
flag = 0;
}
}
}
return 0;
}
题意:和hdu1272差不多,只不过给出的是有向图,问图中的点是否是一颗树。
多记录一下入度判断一下
链接:poj 1308
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <vector>
#include <sstream>
#define ll long long
using namespace std;
const int maxn = 200000 + 10;
const int maxm = 1000000 + 10;
const int inf = 0x3f3f3f3f;
int fa[maxn];
int r[maxn];
int in[maxn];
int num[maxn];
int n, m, k;
void init() {
for(int i = 0; i < maxn; i++) {
fa[i] = i;
in[i] = num[i] = 0;
}
}
int getf(int x) {
if(fa[x] != x) {
fa[x] = getf(fa[x]);
}
return fa[x];
}
bool unions(int x, int y) {
in[y]++;
x = getf(x);
y = getf(y);
if(x == y) {
return false;
}
if(x != y) {
fa[x] = y;
return true;
}
}
int main()
{
int kcase = 0;
int T;
int a, b;
init();
int flag = 1;
while(~scanf("%d %d", &a, &b)) {
if(a == -1 && b == -1) {
break;
}
else if(a == 0 || b == 0) {
int ans = 0;
for(int i = 1; i < maxn; i++) {
if(num[i] && fa[i] == i) {
ans++;
}
if(num[i] && in[i] > 1){
flag = 0;
}
}
if(ans >= 2) {
flag = 0;
}
if(flag) {
printf("Case %d is a tree.\n", ++kcase);
}
else {
printf("Case %d is not a tree.\n", ++kcase);
}
init();
flag = 1;
}
else {
num[a] = num[b] = 1;
if(!unions(a, b)) {
flag = 0;
}
}
}
return 0;
}