voidadd_edges(){ // 纵向是 x 变化的方向, 这里的坐标和一般意义上的坐标系不同 int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1}, dw[] = {1, 2, 1, 2}; for (int z = 0; z <= 1; z++) // n 和 m 真的很容易打错 for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int u = 0; u < 4; u++) // 先为偶 再为奇 if (u % 2 == z) { int x = i+dx[u], y = j+dy[u]; if (x <= 0 || x > n || y <= 0 || y > m) continue; int a = id[i][j], b = id[x][y]; // 按照排列枚举 必然有重复 这里只取一种情况 if (a < b) e[cnt++] = {a, b, dw[u]}; } }
intmain(){ cin >> n >> m; for (int i = 1; i <= n*m; i++) p[i] = i; // 压缩两维到一维 for (int i = 1, t = 0; i <= n; i++) for (int j = 1; j <= m; j++) id[i][j] = t++; // 把初始化边建好 int x1, y1, x2, y2; while (cin >> x1 >> y1 >> x2 >> y2) { int a = id[x1][y1], b = id[x2][y2]; p[find(a)] = find(b); } add_edges(); int res = 0; for (int i = 0; i < cnt; i++) { int pa = find(e[i].a), pb = find(e[i].b); if (pa != pb) { p[pa] = pb; res += e[i].w; } } cout << res << endl; return0; }
intmain(){ cin >> n; for (int i = 1; i <= n; i++) { p[i] = i; int w; cin >> w; e[m++] = {0, i, w}; } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { int w; cin >> w; if (i < j) e[m++] = {i, j, w}; } sort(e, e+m); int res = 0; for (int i = 0; i < m; i++) { int a = find(e[i].a), b = find(e[i].b); if (a != b) { p[a] = b; res += e[i].w; } } cout << res << endl; return0; }
intmain(){ cin >> n >> k; for (int i = 1; i <= n; i++) { p[i] = i; cin >> q[i].x >> q[i].y; for (int j = 1; j < i; j++) { e[m++] = {i, j, q[j].dist(q[i])}; } } sort(e, e+m); int cnt = 0; double res = 0; for (int i = 0; i < m && cnt < n-k; i++) { int a = find(e[i].a), b = find(e[i].b); if (a != b) { p[a] = b; cnt++; res = e[i].w; } } printf("%.2lf\n", res); return0; }
最小生成树的边权和最小完全图
给定一棵 N 个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
intmain(){ int T; cin >> T; while (T--) { cin >> n; for (int i = 1; i <= n; i++) p[i] = i, sz[i] = 1; for (int i = 0; i < n-1; i++) { cin >> e[i].a >> e[i].b >> e[i].w; } sort(e, e+n-1); int res = 0; for (int i = 0; i < n-1; i++) { int a = find(e[i].a), b = find(e[i].b); if (a != b) { p[a] = b; res += (sz[b]*sz[a]-1)*(e[i].w+1); sz[b] += sz[a]; } } cout << res << endl; } return0; }
// 求从 u 出发路径上的最大值和次大值 voiddfs(int u, int par, int maxd1, int maxd2, int dist1[], int dist2[]){ dist1[u] = maxd1; dist2[u] = maxd2; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == par) continue; // 这个地方不要直接改 maxd1 maxd2 因为下一轮循环要用原始数据 int t1 = maxd1, t2 = maxd2; if (w[i] > t1) t2 = t1, t1 = w[i]; elseif (t2 < w[i] && w[i] < t1) t2 = w[i]; dfs(j, u, t1, t2, dist1, dist2); } }
intmain(){ cin >> n >> m; for (int i = 1; i <= n; i++) p[i] = i; memset(h, -1, sizeof h); for (int i = 0; i < m; i++) { int a, b, w; cin >> a >> b >> w; edges[i] = {a, b, w}; } sort(edges, edges+m); // 最多 500 个结点 每条边的范围是 1~1e9 所以会爆 int ll S = 0; for (int i = 0; i < m; i++) { int a = edges[i].a, b = edges[i].b, w = edges[i].w; int pa = find(a), pb = find(b); if (pa != pb) { p[pa] = pb; S += w; edges[i].f = true; add(a, b, w), add(b, a, w); } } // 默认最大距离设成 -1e9 要不然不知道是真的 0 还是默认为 0 for (int i = 1; i <= n; i++) dfs(i, -1, -1e9, -1e9, dist1[i], dist2[i]); // 枚举非树边 ll res = 1e18; for (int i = 0; i < m; i++) { if (edges[i].f) continue; int a = edges[i].a, b = edges[i].b, w = edges[i].w; // S - wt + w > S if (w > dist1[a][b]) { res = min(res, S-dist1[a][b]+w); } elseif (w > dist2[a][b]) { res = min(res, S-dist2[a][b]+w); } } cout << res << endl; return0; }