最小生成树

Kruskal算法

算法描述:

把所有边从小到大排序,能加就加,用并查集维护联通性,如果一个边两边的点已经在同一个连通块里了就不要这条边了

时间复杂度O(mlogm)

code:

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define maxn 200010
using namespace std;

ll n,m,sum=0,cnt=0;
ll fa[maxn];

struct bcj{
    void init(ll n){
        for(int i=1;i<=n;i++) fa[i]=i;
    }
    ll f(ll x){
        return fa[x]=(fa[x]==x)?x:f(fa[x]);
    }
    bool u(ll x,ll y){
        x=f(x),y=f(y);
        if(x==y) return false;
        fa[x]=y;
        return true;
    }
}s;

struct edge{
    ll u,v,w;
    bool friend operator < (edge a,edge b){
        return a.w<b.w;
    }
}e[maxn];

int main(){
    cin>>n>>m;
    s.init(n);
    for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w;
    sort(e+1,e+m+1);
    for(int i=1;i<=m;i++)
        if(s.u(e[i].u,e[i].v)){
            sum+=e[i].w;
            cnt++;
        }
    if(cnt!=n-1){
        cout<<"orz"<<endl;
        return 0;
    }
    cout<<sum<<endl;
    return 0;
} 

 

Prim算法

算法描述:

随便找一个点当树根,接下来每次加入一个新点,选择离当前生成树最近的点加入就可以了

时间复杂度O(n2),可用二叉堆优化到O(mlogn),主要用于稠密图尤其是完全图的最小生成树的求解

code1:

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
#define maxn 200010
using namespace std;
template<typename T>
inline void read(T &x){
    x=0; bool flag=0; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    if(flag) x=-x;
}

struct data{
    ll dis;ll id;
    bool friend operator < (data a,data b){
        return a.dis>b.dis;
    } 
};
priority_queue <data> pq;
ll v[maxn<<1],x[maxn<<1],w[maxn<<1],al[maxn],ct;//maxn<<1相当于maxn*2 
bool visit[maxn];
void add(ll U,ll V,ll va){
    v[++ct]=V;
    x[ct]=al[U];
    al[U]=ct;
    w[ct]=va;
}

int main(){
    ll n,m,u1,v1,w1,ans=0,cnt=0;
    read(n);read(m);
    for(int i=1;i<=m;i++){
        read(u1);read(v1);read(w1);
        add(u1,v1,w1);add(v1,u1,w1);//
    }
    pq.push((data){0,1});
    while(!pq.empty()){
        ll u=pq.top().id;
        ans+=pq.top().dis;
        cnt++;
        visit[u]=true;
        pq.top();
        for(ll i=al[u];i;i=x[i]){
            if(!visit[v[i]])
                pq.push((data){w[i],v[i]});
        }
        while(!pq.empty()&&visit[pq.top().id])
            pq.pop();
    }
    if(cnt<n){
    cout<<"orz"<<endl;
    return 0;
    }
    cout<<ans<<endl;
    return 0;
}

 

code2:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define maxn 200010
using namespace std;
template<typename T>
inline void read(T &x){
    x=0; bool flag=0; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    if(flag) x=-x;
}

int n,m,x,y,z,t,ans=0,d[maxn],a[20010][20010];
bool visit[maxn];

void prim(){
    memset(d,0x3f,sizeof(d));
    d[1]=0;
    for(int i=1;i<=n;i++){
        t=0;
        for(int j=1;j<=n;j++)
            if((!visit[j])&&(t==0||d[j]<d[t]))
                t=j;
        visit[t]=true;
        for(int s=1;s<=n;s++)
            if(!visit[s])
                d[s]=min(d[s],a[t][s]);
    }
}

int main(){
    read(n);read(m);
    memset(a,0x3f,sizeof(a));
    for(int i=1;i<=n;i++) a[i][i]=0;
    for(int i=1;i<=m;i++){
        read(x),read(y),read(z);
        a[y][x]=a[x][y]=min(a[x][y],z);
    }
    prim();
    for(int i=2;i<=n;i++) ans+=d[i];
    cout<<ans<<endl;
    return 0;
}

虽然code2用邻接矩阵存看起来会比code1好懂亿点点,但是n和m稍微大点它就MLE了啊QAQ

 

Boruvka/Sollin算法

(以下内容来自课件)

• 特化算法,用来针对一些奇怪的最小生成树问题

• 1.找到距离每个点最近的点

• 2.使用并查集维护连通性并缩点

• 3.重复上述步骤,只不过这次对于每个子树找到离他最近的点

• 由于每次循环联通块数目至少折半,因此复杂度是对的

• 适用于解决最小异或生成树这样的棘手问题

 

posted @ 2021-02-07 22:47  DReamLion  阅读(54)  评论(0编辑  收藏  举报