在存在权值相等的边时,最小生成树可能有多个。话不多说,求它个数的方法如下:
先用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;
}

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

被折叠的 条评论
为什么被折叠?



