最小生成树个数

当存在权值相等的边时,最小生成树可能不唯一。通过Prim算法生成最小树,结合并查集记录边的关系,找到权值相同的边,移除后判断能否通过其他边连接,以此计算最小生成树的个数。复杂度为O(E log E)。

在存在权值相等的边时,最小生成树可能有多个。话不多说,求它个数的方法如下:

先用Prim生成最小树,同时记录边的关系。用并查集表示//不要压缩路径

这里写图片描述

让每个元素都指向它的父亲节点。

找出权值相同的边,去除树上的这条边,一棵树就被砍成了两颗,然后看加上另一条是否能连接这科树

这里写图片描述

方法就是这样,复杂度O(|E|log|E|)

具体问题:

输入

第一行:T //代表T组数据
每组数据:

  • V E //代表V个顶点 E条边
  • E行x y z//代表节点x到y的边长

输出

最小生成树的个数

代码

#include<stdio.h>
#include<algorithm>
#include<queue>
#define MAX_V 102
#define MAX_E 10002
using namespace std;
struct cell{int x,y,cost;};
int par[MAX_V];
bool used[MAX_V];
cell cs[MAX_E],vs[MAX_V];//保存最小生成树的边
int E,V;

//不要压缩路径 通过并查集表示生成树
void init(int n){for(int i=0;i<=n;i++) par[i]=i;}
int find(int x){return x==par[x]?x:find(par[x]);}
void unite(int x,int y){par[y]=x;}
bool same(int x,int y){return find(x)==find(y);}

bool cmp(cell a,cell b){return a.cost<b.cost;}
bool operator < (cell a,cell b){return a.cost>b.cost;}
int prim(){
    fill(used,used+V+1,false);
    init(V);
    priority_queue<cell> que;
    que.push({1,1,0});
    int u=0;

    while(!que.empty()){
        cell e=que.top();que.pop();
        int t=e.y;
        if(used[t]) continue;
        used[t]=true;
        unite(e.x,e.y);
        vs[u++]=e;

        for(int i=0;i<E;i++)//这儿可以用其他数据结构优化
            if(cs[i].x==t&&!used[cs[i].y])
                que.push({t,cs[i].y,cs[i].cost});
    }

    E/=2;
    sort(cs,cs+E,cmp);
    sort(vs,vs+V,cmp);
    int j=0,k=0,N=1;
    while(j<E&&k<V){
        int va=cs[j].cost,vb=vs[k].cost;
        if(va==vb){
            if(!(cs[j].x==vs[k].x&&cs[j].y==vs[k].y)){
                par[vs[k].y]=vs[k].y;//断开
                if(!same(cs[j].x,cs[j].y)) N++;
                par[vs[k].y]=vs[k].x;//接上
            }
            j++;
        }else if(va<vb){
            j++;
        }else{
            k++;
        }
    }
    return N;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&V,&E);
        for(int i=0;i<E;i++){
            scanf("%d%d%d",&cs[i].x,&cs[i].y,&cs[i].cost);
            cs[E+i].y=cs[i].x;
            cs[E+i].x=cs[i].y;
            cs[E+i].cost=cs[i].cost;
        }
        E*=2;
        int ans=prim();
        printf("%d\n",ans);
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值