#include using namespace std; #define int long long #define endl "\n" const int N = 510, M = 10010; int n, m; // 结构体 struct Edge { int a, b, c; bool flag; // 是不是最小生成树中的边 bool operator<(const Edge &t) const { return c < t.c; } } edge[M]; int d1[N][N]; // 从i出发,到达j最短距离 int d2[N][N]; // 从i出发,到达j次短距离 int sum; // 最小生成树的边权和 // 邻接表 int h[N], e[M], w[M], ne[M], idx; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } // 并查集 int p[N]; int find(int x) { if (x == p[x]) return x; return p[x] = find(p[x]); } /* 假设根为s,求出树中任意两点间的最长距离和严格次长距离。 需要配合换根进行枚举操作才会有效果。 s:出发点 u:到达了u点 fa:u的前序节点,防止走回头路 m1:这条路径上已经获取到的最长路径 m2:这条路径上已经获取到的次长路径 */ void dfs(int s, int u, int fa, int m1, int m2) { for (int i = h[u]; ~i; i = ne[i]) { // 枚举u的每一条出边 int v = e[i]; // v为u的对边节点 if (v == fa) continue; // 不走回头路 int t1 = m1, t2 = m2; // 必须要复制出来td1和td2,原因是此轮要分发多个子任务,此m1,m2是多个子任务共享的父亲传递过来的最大和次大值 if (w[i] > t1) t2 = t1, t1 = w[i]; // 更新最大值、次大值 else if (w[i] < t1 && w[i] > t2) t2 = w[i]; // 更新严格次大值 // 记录从s出发点,到v节点,一路上的最长路径和严格次长路径 d1[s][v] = t1, d2[s][v] = t2; // 生命不息,探索不止 dfs(s, v, u, t1, t2); } } signed main() { cin >> n >> m; // 初始化邻接表 memset(h, -1, sizeof h); // Kruskal + 建图 for (int i = 0; i < m; i++) cin >> edge[i].a >> edge[i].b >> edge[i].c; // 按边权由小到大排序 sort(edge, edge + m); // 初始化并查集 for (int i = 1; i <= n; i++) p[i] = i; // Kruskal求最小生成树 for (int i = 0; i < m; i++) { int a = edge[i].a, b = edge[i].b, c = edge[i].c; int pa = find(a), pb = find(b); if (pa != pb) { p[pa] = pb; // 并查集合并 // ①最小生成树的边权和 sum += c; // ②最小生成树建图,无向图,为求最小生成树中任意两点间的路径中最大距离、次大距离做准备 add(a, b, c), add(b, a, c); // ③标识此边为最小生成树中的边,后面需要枚举每条不在最小生成树中的边 edge[i].flag = 1; } } // d1[i][j]和d2[i][j] // 换根,以每个点为根,进行dfs,可以理解为枚举了每一种情况,肯定可以获取到任意两点间的最长路径和严格次长路径 for (int i = 1; i <= n; i++) dfs(i, i, 0, -1e18, -1e18); int res = 1e18; // 预求最小,先设最大 // 枚举所有不在最小生成树中的边,尝试加入a->b的这条直边 for (int i = 0; i < m; i++) if (!edge[i].flag) { int a = edge[i].a, b = edge[i].b, c = edge[i].c; if (c > d1[a][b]) // 最小生成树外的一条边,(a->b),如果比最小生成树中a->b的最长边长,就有机会参加评选次小生成树。 // 最终的选举结果取决于它增加的长度是不是最少的 res = min(res, sum + c - d1[a][b]); // 替换最大边 else if (c > d2[a][b]) // 替换严格次大边 res = min(res, sum + c - d2[a][b]); // 严格次小生成树的边权和 } // 输出 printf("%lld\n", res); }