http://ac.jobdu.com/problem.php?pid=1249
题目1249:次小生成树
时间限制:1 秒
内存限制:32 兆
特殊判题:否
提交:140
解决:37
-
题目描述:
-
最小生成树大家都已经很了解,次小生成树就是图中构成的树的权值和第二小的树,此值也可能等于最小生成树的权值和,你的任务就是设计一个算法计算图的最小生成树。
-
输入:
-
存在多组数据,第一行一个正整数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
比起次小生成树模板题,就多了个判断是否存在次小生成树。
判断是否存在最小生成树,分两种情况
第一种,最小生成树不存在,那次小生成树显然不存在。
第二种,最小生成存在,但不存在次小生成树,那肯定是所有的边刚好形成最小生成树,没有边剩余。
关于求最小生成树的算法,我这里用了两次kruskal算法,怎么说呢,第一次用kruskal算法(第一次复杂度是o(Elog(E)))是求最小生成树,并记录下最小生成树的边的序号。容易知道,次小生成树是在最小生成树的基础上替换一条边形成的,所以我们枚举最小生成树的每条边,删除它,并再用kruskal算法(以后复杂度都是o(n),因为前面已经排序过了,就不用再排序了)算出剔除这条边的最小生成树,即剔除这条边所能产生最小的次小生成树,最后都枚举一遍就可以算出最终的次小生成树,我表达不太好,看代码吧。
#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<iostream> using namespace std; const int L=100005,inf=1<<30,maxn=1005; struct node{int s,y,w;}edge[L]; int Fa[maxn],n,m,id[maxn],idPos; void init(); int Find(int x); void unite(int x,int y); bool cmp(node a,node b); bool same(int x,int y); int kruskal(); int secondTree(int pos) { init(); // sort(edge,edge+m,cmp); int sum=0,cnt=0; for(int i=0;i<m;i++) { if(cnt==n-1) break; if(i==pos) continue; if(!same(edge[i].s,edge[i].y)) { unite(edge[i].s,edge[i].y); sum+=edge[i].w; cnt++; } } if(cnt!=n-1) return -1; return sum; } int main() { int t; scanf("%d",&t); while(t--) { int Min=inf;idPos=0; scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].s,&edge[i].y,&edge[i].w); int mst=kruskal(); if(mst==-1) {printf("-1\n");continue;} // printf("mst=%d\n",mst); for(int i=1;i<=idPos;i++) { int secmst=secondTree(id[i]); if(secmst!=-1) Min=min(Min,secmst); } if(Min!=inf) printf("%d\n",Min); else printf("-1\n"); } return 0; } void init()//初始化并查集 { for(int i=0;i<=n;i++) Fa[i]=i; } int Find(int x)//查询属于哪个集合,并直接拜“祖宗”为师 { if(Fa[x]==x) return x; else return Fa[x]=Find(Fa[x]); } void unite(int x,int y)//合并x,y两个元素 { x=Find(x);y=Find(y); if(x==y) return ; Fa[y]=x; } bool same(int x,int y)//【判断是否属于同个集合 { return Find(x)==Find(y); } bool cmp(node a,node b) { return a.w<b.w; } int kruskal() { init(); sort(edge,edge+m,cmp); int sum=0,cnt=0; for(int i=0;i<m;i++) { if(cnt==n-1) break; if(!same(edge[i].s,edge[i].y)) { unite(edge[i].s,edge[i].y); sum+=edge[i].w; cnt++; id[++idPos]=i; } } if(cnt!=n-1) return -1; return sum; }