这套模板适合所有使用并查集的题目,无论是检测是否连通,还是构造最小生成树。
节点怎么定义
1、利用节点ID定义;
2、若节点输入为char,转换为1-n的整数;
3、若没有给出明确的节点定义特征,可以使用输入顺序order表示两点,请看例题:Freckles
是否调用queue
1、若需要求最小生成树,例如最小消耗,最短距离等,可以使用priority_queue,其具体用法可自行搜索;
2、若仅判断图是否连通,还有多少顶点未连通,可以不调用queue库
#include <iostream>
#include <queue> // 一会调用优先队列
using namespace std;
#define MAX 100 // 最大尺寸,在题目给出数据过多时,可自行调整
int root[MAX]; // 每棵树的根节点
int high[MAX]; // 每棵树的高度
void Initial(int n) // 初始化
{
for(int i = 0;i<=n;i++) // 一般从1开始到n,也有从0到n-1的,全部包括进去,检查时去除边界点
{
root[i] = i;
high[i] = 0;
}
}
int find(int x) // 找到自己的根节点
{
if( x != root[x])
root[x] = find(root[x]); // 递归调用寻找根节点
return root[x];
}
bool Union(int x,int y) // 在一些简单情况下可以使用不包括返回值的联通函数
{
x = find(x); // x的跟
y = find(y); // y的跟
if(x == y) return false; // 二者是同一棵树,无需连通
else
{
if(high[x] < high[y]) // 把树x作为y的子树
// 注意:x树仅根节点变化,其他分支及叶节点跟还是指向x,但是在find调用时会修改掉
{
root[x] = y;
}
else if(high[x] > high[y])
{
root[y] = x;
}
else // 二者等高
{
root[x] = y; // x作为y的子树
high[y]++; // y最大高度加一
}
return true; // 传进来的x,y需要联通
}
}
struct edge // 边界点
{
int start,end,cost; // 起点,终点,花费
edge(int x,int y,int z)
{
start = x;
end = y;
cost = z;
}
edge(){}
bool operator<(const edge& e) const // 为了调用优先队列重定义<
{
return cost > e.cost;
}
};
int main()
{
int n,m,min_cost,len;
edge e;
while(cin >> n >> m)
{
priority_queue<edge> q; // 根据重定义后的结果,此时cost小的路径在顶部
Initial(m); // 初始化
for(int i = 0;i<n;i++)
{
cin >> e.start >> e.end >> e.cost;
q.push(e);
}
if(n < m-1) // 无向图连通最小边数为顶点数-1
{
cout << "?" << endl;
continue;
}
len = m-1; // 最小边数,当len为0时,代表图中已经有顶点-1条边,连通
min_cost = 0; // 最小花费
while(len && !q.empty())
{
e = q.top(); // 克鲁斯卡尔算法,每次取最小路径
q.pop();
if(Union(e.start, e.end)) // 路径两端是否连通
{
len--; // 边加一
min_cost += e.cost; // 修路
}
}
if(len) cout << "?" << endl; // len == 0表示图连通,否则不连通
else cout << min_cost << endl;
}
}


京公网安备 11010502036488号