题目1249:次小生成树

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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值