You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.9 KiB

2 years ago
#include <bits/stdc++.h>
using namespace std;
2 years ago
#define int long long
#define endl "\n"
2 years ago
const int N = 510, M = 10010;
int n, m;
// 结构体
struct Edge {
2 years ago
int a, b, c;
2 years ago
bool flag; // 是不是最小生成树中的边
bool operator<(const Edge &t) const {
2 years ago
return c < t.c;
2 years ago
}
2 years ago
} edge[M];
2 years ago
int d1[N][N]; // 从i出发到达j最短距离
int d2[N][N]; // 从i出发到达j次短距离
2 years ago
int sum; // 最小生成树的边权和
2 years ago
// 邻接表
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);
}
}
2 years ago
signed main() {
cin >> n >> m;
2 years ago
// 初始化邻接表
memset(h, -1, sizeof h);
// Kruskal + 建图
for (int i = 0; i < m; i++)
2 years ago
cin >> edge[i].a >> edge[i].b >> edge[i].c;
2 years ago
// 按边权由小到大排序
sort(edge, edge + m);
// 初始化并查集
for (int i = 1; i <= n; i++) p[i] = i;
// Kruskal求最小生成树
for (int i = 0; i < m; i++) {
2 years ago
int a = edge[i].a, b = edge[i].b, c = edge[i].c;
2 years ago
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb; // 并查集合并
// ①最小生成树的边权和
2 years ago
sum += c;
2 years ago
// ②最小生成树建图,无向图,为求最小生成树中任意两点间的路径中最大距离、次大距离做准备
2 years ago
add(a, b, c), add(b, a, c);
2 years ago
// ③标识此边为最小生成树中的边,后面需要枚举每条不在最小生成树中的边
edge[i].flag = 1;
}
}
// d1[i][j]和d2[i][j]
// 换根以每个点为根进行dfs,可以理解为枚举了每一种情况,肯定可以获取到任意两点间的最长路径和严格次长路径
for (int i = 1; i <= n; i++) dfs(i, i, 0, -1e18, -1e18);
2 years ago
int res = 1e18; // 预求最小,先设最大
2 years ago
// 枚举所有不在最小生成树中的边,尝试加入a->b的这条直边
for (int i = 0; i < m; i++)
if (!edge[i].flag) {
2 years ago
int a = edge[i].a, b = edge[i].b, c = edge[i].c;
2 years ago
if (c > d1[a][b]) // 最小生成树外的一条边,(a->b),如果比最小生成树中a->b的最长边长就有机会参加评选次小生成树。
2 years ago
// 最终的选举结果取决于它增加的长度是不是最少的
2 years ago
res = min(res, sum + c - d1[a][b]); // 替换最大边
else if (c > d2[a][b]) // 替换严格次大边
res = min(res, sum + c - d2[a][b]); // 严格次小生成树的边权和
2 years ago
}
// 输出
printf("%lld\n", res);
}