1.并查集
"在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。有一个联合-查找算法(union-find algorithm)定义了两个用于此数据结构的操作:
Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。
由于支持这两种操作,一个不相交集也常被称为联合-查找数据结构(union-find data structure)或合并-查找集合(merge-find set)。其他的重要方法,MakeSet,用于创建单元素集合。有了这些方法,许多经典的划分问题可以被解决。
为了更加精确的定义这些方法,需要定义如何表示集合。一种常用的策略是为每个集合选定一个固定的元素,称为代表,以表示整个集合。接着,Find(x) 返回 x 所属集合的代表,而 Union 使用两个集合的代表作为参数。" --------维基百科
1.初始化每个门派的代表元素
void init()//初始化每一个集合的门派代表
{
for (int i = 1; i <= N; i++)
father[i] = i;
}
2 找到每个元素所属的门派
int find(int x)//找门派代表
{
while (father[x] != x) x = father[x];
//if(father[x]!=x) father[x]=find(father[x]);//递归写法
return x;
}
3 认主归宗,找到组织
void merge(int x, int y)
{
//cout << "merge " << find(x) << " " << find(y) << endl;//before merge 你可以看看
int a, b;
a = find(x), b = find(y);//找到x的门派,再找到y的门派
//if (a == b)//本是同一家的就不用合并,所以略去
//return;
if(a!=b)
father[a] = b;//不是的话,就把y归为x门下
//cout << "merge " << find(x) << " " << find(y) << endl;//after merge
}
来一道开胃蔡:P3367 【模板】并查集
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数N、M,表示共有N个元素和M个操作。
接下来M行,每行包含三个整数Zi、Xi、Yi
当Zi=1时,将Xi与Yi所在的集合合并
当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N
输出格式
如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N
输入输出样例
输入 #1 复制
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
输出 #1 复制
N
Y
N
Y
说明/提示
时空限制:1000ms,128M
数据规模:
对于30%的数据,N<=10,M<=20;
对于70%的数据,N<=100,M<=1000;
对于100%的数据,N<=10000,M<=200000。
#include <iostream>
using namespace std;
const int N = 200005;
int father[N];
void init()//初始化每一个集合的门派代表
{
for (int i = 1; i <= N; i++)
father[i] = i;
}
int find(int x)//找门派代表
{
while (father[x] != x) x = father[x];
return x;
}
void merge(int x, int y)
{
//cout << "merge " << find(x) << " " << find(y) << endl;//before merge
int a, b;
a = find(x), b = find(y);//找到x的门派,再找到y的门派
//if (a == b)//本是同一家的就不用合并
//return;
if(a!=b)
father[a] = b;//不是的话,就把y归为x门下
//cout << "merge " << find(x) << " " << find(y) << endl;//after merge
}
int main()
{
int n, m;
cin >> n >> m;
int x, y, z;
init();
for (int i = 0; i < m; i++)
{
cin >> z >> x >> y;
if (z & 1)
merge(x, y);
else
if (find(x) == find(y))
cout << "Y\n";
else
cout << "N\n";
}
system("pause");
return 0;
}
基本的差不多就这样。
如果想深入了解学习 可以看看大佬的博客 并查集(Union-Find Algorithm)
2.最小生成树
最小生成树是一副连通加权无向图中一棵权值最小的生成树。
最小生成树其实是最小权重生成树的简称。
假设 nn 表示图中点数,mm 表示图中边数。
Prim算法
适用于稠密图,时间复杂度 O(n2)O(n2)。
核心思想:每次挑一条与当前集合相连的最短边。
C++ 代码
// st[i] 表示点i是否在当前生成树集合中
// dist[i] 表示点i到当前集合的最短边的长度
// g[i][j] 表示点i和点j之间边的长度
// 返回值:最小生成树中所有边的总长度
int Prim()
{
int res = 0;
for (int i = 1; i <= n; i ++ )
{
dist[i] = INF;
st[i] = false;
}
dist[1] = 0;
for (int i = 1; i <= n; i ++ )
{
int id = -1, min_dist = INF;
// 寻找最短边
for (int j = 1; j <= n; j ++ )
if (!st[j] && dist[j] < min_dist)
{
id = j;
min_dist = dist[j];
}
st[id] = true;
res += dist[id];
// 用新加入的点更新其余点到生成树的最短边
for (int j = 1; j <= n; j ++ )
if (!st[j])
dist[j] = min(dist[j], g[id][j]);
}
return res;
}
Kruskal算法
适用于稀疏图,时间复杂度 O(mlogm)O(mlogm)。
核心思想:从小到大挑不多余的边。
C++ 代码
// 边的信息
struct Edge
{
int a, b, v;
bool operator< (const Edge &W) const
{
return v < W.v;
}
};
// 并查集——寻找当前集合的代表元素
int find(int x)
{
if (father[x] != x) father[x] = find(father[x]);
return father[x];
}
// 所有边存储在 Edge edges[M];
// 函数返回最小生成树中所有边的总长度
int Kruskal()
{
int res = 0;
// 初始化并查集代表元素
for (int i = 1; i <= n; i ++ ) father[i] = i;
sort(edge, edge + m);
for (int i = 0; i < m; i ++ )
{
int a = edge[i].a, b = edge[i].b;
if (find(a) != find(b))
{
res += edge[i].v;
father[find(a)] = find(b);
}
}
return res;
}
这是一道模板题,详情可点击 洛谷 P3366
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5001,M = 200005;
int father[N];
int n, m;
struct Edge//图
{
int x, y, z;
bool operator <(const Edge &e) const
{
return z < e.z;
}
}E[M];
void init()
{
for (int i = 0; i < n; i++)
father[i] = i;
}
int find(int x)
{
while (father[x] != x) x = father[x];
return father[x];
}
void merge(int x, int y)
{
int a = find(x), b = find(y);
if (a != b)
father[a] = b;
}
int Kruskal()
{
init();
int ans=0;
sort(E, E + m);
for (int i = 0; i < m; i++)
{
if (find(E[i].x) != find(E[i].y))
{
ans += E[i].z;
merge(E[i].x, E[i].y);
}
}
return ans;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < m; i++)
cin >> E[i].x >> E[i].y >> E[i].z;
cout << Kruskal();
system("pause");
return 0;
}