题解中有很多说最优解是kruskal重构树
所以
抽了个早自习看了看这方面的内容
感觉真的挺好使的
首先对于kruskal算法来说
是基于贪心的思想把边权排序用并查集维护是否是在同一棵树上
对于kruskal重构树来说
按不同边权顺序排序可相应的得到最大边权的最小值 、最小边权的最大值等问题
建树过程:
排好序后, 遍历, 若两条边u, v不在同一并查集内, 那么就新建一个节点, 这个节点的点权就代表u到v的边权, 同时将这三个点都加入同一并查集
需要注意
最后建立出来的可能是森林,也就是并不是所有的点的根都是相同的
此时
可以用一个vis数组来确保所有的点都被遍历过了
1.若按照边权的升序排列
那么最后可以求得最大边权的最小值
感性理解一下
因为最后是要求最小值
所以
建一棵最小生成树
此时最大的边权就是所有别的建树方法中的最小值
关于为什么lca是答案:
感性理解一下
如果我们建的树是最小生成树
那么
显然边权越大深度越深
我们是想按着边权尽量小的走
lca(u, v)是原图u->v节点深度最小的点
所以就是他了
2.若按照边权的降序排列
同上
要求最小边权的最大值
最后答案是最大值
那么建一棵最大生成树
关于此题的代码:
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; const int N = 500010; int n, m, sum, fa[N], cnt, size[N], top[N], dad[N], head[N], va[N], tot, q, deep[N], vis[N], son[N]; struct Edge{ int from, to, w; }e[N << 1]; struct Node { int nxt, to, w; }ee[N]; void add(int x, int y) { ee[++tot].nxt = head[x]; ee[tot].to = y; head[x] = tot; } bool cmp(Edge x, Edge y) { return x.w > y.w; } int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void dfs(int u, int pa) { size[u] = 1;vis[u] = 1; for(int i = head[u]; i; i = ee[i].nxt) { int v = ee[i].to; if(v == pa) continue; deep[v] = deep[u] + 1, dad[v] = u; dfs(v, u); size[u] += size[v]; if(size[v] > size[son[u]]) son[u] = v; } } void dfs1(int u, int tp) { top[u] = tp; if(son[u]) dfs1(son[u], tp); for(int i = head[u]; i; i = ee[i].nxt) { int v = ee[i].to; if(v == dad[u] || v == son[u]) continue; dfs1(v, v); } } int lca(int x, int y) { while(top[x] != top[y]) { if(deep[top[x]] < deep[top[y]]) swap(x, y); x = dad[top[x]]; } return deep[x] > deep[y] ? y : x; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) fa[i] = i; cnt = n; for(int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].w); sort(e + 1, e + 1 + m, cmp); for(int i = 1; i <= m; i++) { int fx = find(e[i].from), fy = find(e[i].to); if(fx != fy) { va[++cnt] = e[i].w; fa[fx] = fa[fy] = fa[cnt] = cnt; add(fx, cnt);add(cnt, fx); add(fy, cnt);add(cnt, fy); } } for(int i = 1; i <= cnt; i++) if(!vis[i]) { int f = find(i); dfs(f, 0), dfs1(f, f); } /*for(int i = 1; i <= cnt; i++) printf("%d ", size[i]);*/ scanf("%d", &q); while(q--) { scanf("%d%d", &n, &m); if(find(n) != find(m)) printf("-1\n"); else printf("%d\n", va[lca(n, m)]); } return 0; }
谢谢收看, 祝身体健康!
原文:https://www.cnblogs.com/yanxiujie/p/11438055.html