九度OJ 1249:次小生成树 (次小生成树)
- 题目描述:
-
最小生成树大家都已经很了解,次小生成树就是图中构成的树的权值和第二小的树,此值也可能等于最小生成树的权值和,你的任务就是设计一个算法计算图的最小生成树。
- 输入:
-
存在多组数据,第一行一个正整数t,表示有t组数据。
每组数据第一行有两个整数n和m(2<=n<=100),之后m行,每行三个正整数s,e,w,表示s到e的双向路的权值为w。
- 输出:
-
输出次小生成树的值,如果不存在输出-1。
- 样例输入:
-
2 3 3 1 2 1 2 3 2 3 1 3 4 4 1 2 2 2 3 2 3 4 2 4 1 2
- 样例输出:
-
4 6
思路:
求给定图的次小生成树。
对于给定的图,我们可以证明,次小生成树可以由最小生成树变换一边得到。那么我们可以如下求给定图的次小生成树。首先,我们用prime算法求出图的最小生成树,在这个过程中记录每条边是否用过,以及两个点之间最短路径上的最大权值F[i,j]
F[i,j]可以如此求得,当加入点u的时候,并且u的父结点是v 那么对于已经在生成树中的节点x
F[x,u] = max(F[x,v], weight[u][v]),那么我么就可以用Prime算法一样的时间复杂度来求出图的次小生成树。
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define INF 0x3f3f3f3f int T, n, m; int G[110][110], F[110], M[110][110], mark[110]; int max(int x, int y) { return x > y ? x : y; } void dfs(int src, int pre, int now) { if (mark[now]) return; //printf("dfs %d %d\n", src, now); if (src == now) { M[src][now] = M[now][src] = 0; }else { M[src][now] = max(G[pre][now], M[src][pre]); M[now][src] = M[src][now]; //printf("update M(%d,%d) = %d\n", now, src, M[src][now]); } mark[now] = 1; int i; for (i=1; i<=n; i++) if (F[i] == now || F[now] == i) { dfs(src, now, i); } } int main() { scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); memset(G, INF, sizeof G); int i, j; for (i=0; i<m; i++) { int a, b, c; scanf("%d%d%d", &a, &b, &c); G[a][b] = G[b][a] = c; } // prim int dist[110], min_sum=0; memset(dist, INF, sizeof dist); memset(F, -1, sizeof F); memset(mark, 0, sizeof mark); dist[1] = 0; while (1) { j = 0; for (i=1; i<=n; i++) if (!mark[i] && dist[i] < dist[j]) j = i; if (j == 0) break; min_sum += dist[j]; mark[j] = 1; for (i=1; i<=n; i++) if (!mark[i] && dist[i] > G[j][i]) { dist[i] = G[j][i]; F[i] = j; } } // now we get the max_path of i to j memset(M, 0, sizeof M); for (i=1; i<=n; i++) { memset(mark, 0, sizeof mark); dfs(i, i, i); } int ans = INF; // try delete i-j for (i=1; i<=n; i++) for (j=1; j<=n; j++) { if (i!=j && F[j] != i && F[i] != j && min_sum + G[i][j] - M[i][j] < ans) { ans = min_sum + G[i][j] - M[i][j]; //printf("ans : min_sum + G[%d][%d] - M[%d][%d] = %d\n", i, j, i, j, ans); } } printf("%d\n", ans); } return 0; } /************************************************************** Problem: 1249 User: warcraftw Language: C Result: Accepted Time:20 ms Memory:1008 kb ****************************************************************/
编程算法爱好者。