这套模板适合所有使用并查集的题目,无论是检测是否连通,还是构造最小生成树。
节点怎么定义
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; } }