题意:
给你一张无向图,让你判断三种情况:1.不是连通图(无法形成生成树)2.只能生成唯一的生成树 3.能生成的生成树不唯一(有次小生成树),这种情况要求出次小生成树的边权值和。
思路:
比较常见的次小生成数做法:先求出最小生成树,再依次使用不在最小生成树上的边与最小生成树连接,连接后必然出现且仅出现一个环(由于生成树上的任意两点之间都有唯一的一条路径,且图中所有的点都在生成树上),将这条边与环上除了这条边权值最大的边替换,就形成了新的生成树,在不断尝试新边的过程中维护一个最小的生成数的边权值和即是次小生成树的边权值和。
可以发现生成的环形成的路径即是两个端点与它们的LCA(最近公共祖先)的路径,所以可以用求LCA的办法顺便记录路径中边权的最大值。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <queue> 6 typedef long long ll; 7 using namespace std; 8 9 const int N=1e2+5; 10 11 struct edge { 12 int id; 13 int from; 14 int to; 15 int val; 16 }E[N<<2]; 17 18 struct cmp { 19 bool operator()(edge a,edge b) { 20 return a.val>b.val; 21 } 22 }; 23 24 int fir[N],nex[N<<2],cnt; 25 int pre[N],dis[N],dep[N]; 26 bool vis[N],used[N<<1]; 27 int T=0,t,n,m,cost; 28 29 void init() { 30 memset(fir,-1,sizeof(fir)); 31 cost=cnt=0; 32 } 33 34 void connect(int from,int to,int val,int id) { 35 E[cnt]=(edge){id,from,to,val}; 36 nex[cnt]=fir[from]; 37 fir[from]=cnt++; 38 E[cnt]=(edge){id,to,from,val}; 39 nex[cnt]=fir[to]; 40 fir[to]=cnt++; 41 } 42 43 bool prim() { 44 int node=0;记录生成树上的点的数量。 45 memset(vis,false,sizeof(vis)); 46 memset(used,false,sizeof(used)); 47 priority_queue <edge,vector <edge>,cmp> Q; 48 Q.push((edge){-1,0,1,0}); 49 dep[0]=0; 50 while(!Q.empty()) { 51 edge q=Q.top(); 52 Q.pop(); 53 if(vis[q.to]) continue; 54 vis[q.to]=true; 55 cost+=q.val; node++; 56 pre[q.to]=q.from; 57 dis[q.to]=q.val; 58 dep[q.to]=dep[q.from]+1;//在求最小生成树的过程中顺便记录生成树上的点的深度以及父节点、与父节点连接的边的权值。 59 used[q.id]=true;//记录哪些边在最小生成树上,到时在求次小生成树的过程中跳过这些边。 60 for(int i=fir[q.to];i!=-1;i=nex[i]) { 61 int to=E[i].to; 62 if(!vis[to]) Q.push(E[i]); 63 } 64 } 65 if(node<n) return false;生成树上的点少于n,说明不是连通图,无法形成最小生成树。 66 return true; 67 } 68 69 int lca(int x,int y) { 70 int MAX=0; 71 if(dep[x]>dep[y]) swap(x,y); 72 while(dep[y]>dep[x]) { 73 MAX=max(MAX,dis[y]); 74 y=pre[y]; 75 } 76 while(x!=y) { 77 MAX=max(MAX,dis[y]); 78 y=pre[y]; 79 MAX=max(MAX,dis[x]); 80 x=pre[x]; 81 } 82 return MAX; 83 } 84 85 void solve() { 86 bool flag=false; 87 int second=2e9; 88 for(int i=0;i<cnt;i+=2) { 89 if(used[E[i].id]) continue; 90 flag=true; 91 second=min(second,cost+E[i].val-lca(E[i].from,E[i].to)); 92 } 93 if(flag) printf("Case #%d : %d\n",++T,second);//若除了最小生成树上的边以外没有剩下的边,那么没有次小生成树。 94 else printf("Case #%d : No second way\n",++T); 95 } 96 97 int main() { 98 scanf("%d",&t); 99 while(t--) { 100 scanf("%d%d",&n,&m); 101 init(); 102 for(int i=1;i<=m;i++) { 103 int x,y,z; 104 scanf("%d%d%d",&x,&y,&z); 105 connect(x,y,z,i); 106 } 107 if(!prim()) { 108 printf("Case #%d : No way\n",++T); continue; 109 } 110 solve(); 111 } 112 return 0; 113 }