题意:求一幅无向图的最小生成树与最小生成树,不存在输出-1
解法:用Kruskal求最小生成树,标记用过的边。求次小生成树时,依次枚举用过的边,将其去除后再求最小生成树,得出所有情况下的最小的生成树就是次小的生成树。可以证明:最小生成树与次小生成树之间仅有一条边不同。不过这样复杂度有点高,可达O(m^2).
求次小生成树有O(mlogm+n^2)的算法,详见
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define Mod 1000000007 using namespace std; #define N 507 struct Edge { int s,t,w; }edge[N*N]; int fa[N],use[N],n,m,minedge; //minedge:最小生成树的边数 void init() { for(int i=1;i<=n;i++) fa[i] = i; } int cmp(Edge ka,Edge kb) { return ka.w < kb.w; } int findset(int x) { if(x != fa[x]) fa[x] = findset(fa[x]); return fa[x]; } int Kruskal_MinTree() { int u,v; init(); int i,flag,cnt; minedge = 0; flag = cnt = 0; int tmp = 0; for(i=0;i<m;i++) { u = edge[i].s; v = edge[i].t; int fx = findset(u); int fy = findset(v); if(fx != fy) { use[minedge++] = i; tmp += edge[i].w; fa[fx] = fy; cnt++; } if(cnt == n-1) { flag = 1; //找到最小生成树 break; } } if(flag) return tmp; return -1; //不存在最小生成树,返回-1 } int Sec_MinTree() { int i,j,mini,tmp; int cnt,flag,u,v; mini = Mod; for(i=0;i<minedge;i++) //枚举最小生成树中的每条边,将其去除 { init(); flag = cnt = tmp = 0; for(j=0;j<m;j++) { if(j != use[i]) //去除边 { u = edge[j].s; v = edge[j].t; int fx = findset(u); int fy = findset(v); if(fx != fy) { cnt++; tmp += edge[j].w; fa[fx] = fy; } if(cnt == n-1) { flag = 1; break; } } } if(flag && tmp < mini) mini = tmp; } if(mini == Mod) mini = -1; return mini; } int main() { int i,j; int u,v,w; while(scanf("%d%d",&n,&m)!=EOF) { for(i=0;i<m;i++) { scanf("%d%d%d",&u,&v,&w); edge[i].s = u; edge[i].t = v; edge[i].w = w; } sort(edge,edge+m,cmp); int flag = Kruskal_MinTree(); int mini = Sec_MinTree(); printf("Cost: %d ",flag); printf("Cost: %d ",mini); } return 0; }