次小生成树
次小生成树:通过交换一条最小生成树上的边
求解次小生成树可通过Kruskal算法和prime算法延伸得到:
1、Kruskal 算法求得的过程就是多次求最小生成树。我们第一次求最小生成树的时候把边的序号都记下来,然后以后多次求最小生成树的时候,每次都不用第一次求得的最小生成树的边。比如说,第一次求最小生成树用到了边1 6 7,则我们再次求3次最小生成树,第一次不用边1,第二次不用边6,第三次不用边7,若中间有哪一次和第一次的和相等,则说明不唯一,否则,唯一。(ps:自己还没有用Kruskal算法写过,下次有机会补上)
2、用prime算法求最小生成树的过程是先求一遍最小生成树,在求得过程中我们设置一个pre数组,表示的是某个点的前驱,比如说刚开始选到点1,然后选到的第一条边是从1到3的,则pre[3] = 1,并且在求最小生成树的过程中把选到的边记录下来。我们再设置另一个数组maxedge,表示在构造好的树中,如果x和y之间有一条边,且该边没有在最小生成树中,则maxedge[x][y]表示加上这条边后,所能去掉的最大的边。比如说,我们求出一颗最小生成树后,此时x和y有一条边,且这条边没有被用到,此时maxedge[x][y]表示生成树中从x到y的路径中最大的一条边,也就是说连接x和y的路径中能够去掉的最大的边,也就是说求出最小生成树后,再遍历图中所有没有用到的边求次小生成树即可。如果某个值 和最小生成树的值相同,则最小生成树不唯一,否则最小生成树唯一。

const int MAXN = 110; const int INF = 0x3f3f3f3f; int N, M; int g[MAXN][MAXN], dist[MAXN]; bool vis[MAXN], use[MAXN][MAXN]; int ans, pre[MAXN], maxedge[MAXN][MAXN]; void prime(int n) { for(int i = 1; i <= n; i++) dist[i] = INF; dist[1] = 0; for(int i = 1; i <= n; i++) pre[i] = i; for(int i = 1; i <= n; i++) { int min_dis = INF; int idx; for(int j = 1; j <= n; j++) { if(!vis[j] && min_dis > dist[j]) { min_dis = dist[j]; idx = j; } } for(int j = 1; j <= n; j++) if(vis[j]) { maxedge[idx][j] = maxedge[j][idx] = max(maxedge[j][pre[idx]], dist[idx]); } vis[idx] = 1; use[idx][pre[idx]] = use[pre[idx]][idx] = 1; ans += min_dis; for(int j = 1; j <= n; j++) { if(!vis[j] && dist[j] > g[idx][j]) { dist[j] = g[idx][j]; pre[j] = idx; } } } } int smst(int n) { int Min = INF; for(int i = 1; i <= n; i++) { for(int j = i+1; j <= n; j++) { if(g[i][j] != INF && !use[i][j]) { int res = ans + g[i][j] - maxedge[i][j]; Min = min(Min, res); } } } if(Min == ans) return 0; return 1; } void init() { ans = 0; memset(use, false, sizeof(use)); memset(vis, false, sizeof(vis)); memset(maxedge, 0, sizeof(maxedge)); for(int i = 0; i < MAXN; i++) { for(int j = 0; j < MAXN; j++) { g[i][j] = INF; } } }
以下几题是较为基础的题,套下模板即可,不过读题很重要(第二题一开始都没看出来是次小生成树问题)
1、poj1679The Unique MST(此题较为经典)

#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> using namespace std; const int MAXN = 110; const int INF = 0x3f3f3f3f; int N, M; int g[MAXN][MAXN], dist[MAXN]; bool vis[MAXN], use[MAXN][MAXN]; int ans, pre[MAXN], maxedge[MAXN][MAXN]; void prime(int n) { for(int i = 1; i <= n; i++) dist[i] = INF; dist[1] = 0; for(int i = 1; i <= n; i++) pre[i] = i; for(int i = 1; i <= n; i++) { int min_dis = INF; int idx; for(int j = 1; j <= n; j++) { if(!vis[j] && min_dis > dist[j]) { min_dis = dist[j]; idx = j; } } for(int j = 1; j <= n; j++) if(vis[j]) { maxedge[idx][j] = maxedge[j][idx] = max(maxedge[j][pre[idx]], dist[idx]); } vis[idx] = 1; use[idx][pre[idx]] = use[pre[idx]][idx] = 1; ans += min_dis; for(int j = 1; j <= n; j++) { if(!vis[j] && dist[j] > g[idx][j]) { dist[j] = g[idx][j]; pre[j] = idx; } } } } int smst(int n) { int Min = INF; for(int i = 1; i <= n; i++) { for(int j = i+1; j <= n; j++) { if(g[i][j] != INF && !use[i][j]) { int res = ans + g[i][j] - maxedge[i][j]; Min = min(Min, res); } } } if(Min == ans) return 0; return 1; } void init() { ans = 0; memset(use, false, sizeof(use)); memset(vis, false, sizeof(vis)); memset(maxedge, 0, sizeof(maxedge)); for(int i = 0; i < MAXN; i++) { for(int j = 0; j < MAXN; j++) { g[i][j] = INF; } } } int main() { int T; scanf("%d", &T); while(T--) { init(); scanf("%d%d", &N, &M); for(int i = 1; i <= M; i++) { int u, v, c; scanf("%d%d%d", &u, &v, &c); if(g[u][v] > c) g[u][v]= g[v][u] = c; } prime(N); if(smst(N)) { printf("%d\n", ans); } else puts("Not Unique!"); } return 0; }
2、hdu4081Qin Shi Huang's National Road System

#include <iostream> #include <cmath> #include <cstring> #include <string> #include <cstdio> #define MAXN 1010 using namespace std; const double INF = 999999999; int pre[MAXN]; double dist[MAXN], g[MAXN][MAXN], maxedge[MAXN][MAXN]; bool vis[MAXN], use[MAXN][MAXN]; double ans; struct Point { double x, y; int c; }node[MAXN]; double get_len(const Point &a, const Point &b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } void init() { ans = 0; memset(vis, false, sizeof(vis)); memset(use, false, sizeof(use)); memset(maxedge, 0, sizeof(maxedge)); for(int i = 0; i < MAXN; i++) { for(int j = 0; j < MAXN; j++) { g[i][j] = INF; } } } void prime(int n) { for(int i = 1; i <= n; i++) dist[i] = INF; dist[1] = 0; for(int i = 1; i <= n; i++) pre[i] = i; for(int i = 1; i <= n; i++) { double min_dis = INF; int idx; for(int j = 1; j <= n; j++) { if(!vis[j] && min_dis > dist[j]) { min_dis = dist[j]; idx = j; } } for(int j = 1; j <= n; j++) if(vis[j]) { maxedge[idx][j] = maxedge[j][idx] = max(maxedge[j][pre[idx]], dist[idx]); } vis[idx]= 1; use[idx][pre[idx]] = use[pre[idx]][idx] =1; ans += min_dis; for(int j = 1; j <= n; j++) { if(!vis[j] && dist[j] > g[idx][j]) { dist[j] = g[idx][j]; pre[j] = idx; } } } } double cal(int n) { double ratio = 0; int A; double B; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) if(i != j){ A = node[i].c + node[j].c; if(use[i][j]) { B = ans - g[i][j]; } else { if(g[i][j] != INF) { B = ans - maxedge[i][j]; } } ratio = max(ratio,1.0*A/B); } } return ratio; } int main() { int T, n; scanf("%d", &T); while(T--) { init(); scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%lf %lf%d", &node[i].x, &node[i].y, &node[i].c); } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { g[i][j] = get_len(node[i], node[j]); } } prime(n); printf("%.2lf\n", cal(n)); } return 0; }