最小生成树入门 kruskal和堆优化的prim

定义:给定一个无向图,如果它的某个子图中的任意两个顶点都互相连通并且是一棵树,那么这棵树就叫生成树(Spanning Tree)如果边存在权值,那么使得边权和最小的生成树叫做最小生成树(MST Minimum Spanning Tree)。

求最小生成树的算法有两种:Prim和Kruskal。Prim在稠密图中效率更高,Kruskal在稀疏图中效率更高。

Prim类似于Dijkstra,从某个顶点出发,维护一个权值数组d[v],寻找这个顶点周围权值最小的边,然后更新顶点,继续下去,当所有点都被更新时,d[v]就是最后的结果。

Kruskal就是把边权排序,从最小的边权开始,按照大小连接,当连接点为n-1个时,树就完成了,当中用并查集处理数据,连起来的点放入同一个集合(连入同一个根节点)。

#include<bits/stdc++.h>

using namespace std;
#define MAXN 10000005
int pa[MAXN],ra[MAXN],a[MAXN],n,k,ans,cnt;
bool is_prime[MAXN];
vector<int> prime;
struct edge
{
    int x,y,z;
}mp[MAXN];

int init(int n)
{
    for(int i=1; i<=n; i++)
    {
        pa[i]=i;
        ra[i]=0;
    }
}

int Find(int x)
{
    return pa[x]==x ? x : pa[x]=Find(pa[x]);
}

bool cmp(const edge &x,const edge &y)
{
    return x.z<y.z;
}

void kruskal()
{
    sort(mp,mp+k,cmp);           //排序
    for(int i=0;i<k;i++)
    {
         int x=Find(mp[i].x),y=Find(mp[i].y);           //取出根节点
         if(x==y) continue;                            //如果已经加入集合就跳过
         ans+=mp[i].z;
         pa[y]=x;
         cnt++;                                        //记录节点数
         if(cnt==n-1) break;
    }
}

 Prim呢,就类似于dijkstra,我用的是使用堆优化的prim(其实就是用优先队列优化了dijkstra)邻接表存图,然后类似于dijkstra的方式比较距离起点最近的边,只不过把边按照边权排序,连满n条边时,这个生成树就是最小的。

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int>pii;
#define INF 2147483647
int d[1000005],vis[1000005],cnt,sum,n,m;
vector<pii>e[1000005];

struct cmp                                          //自定义排序方法 因为我定义的优先队列里,边权和是第二个元素,如果直接greater,它会默认按第一个元素排序
{
    bool operator()(pii &a,pii &b)
    {
        return a.second>b.second;
    }
};

priority_queue <pii,vector<pii>,cmp > q;

void add_edge(int x, int y, int z)                 //邻接表存图
{
    e[x].push_back(make_pair(y, z));
    e[y].push_back(make_pair(x, z));
}

void init(int n)
{
    for (int i = 1; i <= n; i++) e[i].clear();     //初始化
    for (int i = 1; i <= n; i++) d[i] = INF;
}

void prim()
{
    d[1]=0;
    q.push(make_pair(1,0));
    while(!q.empty()&&cnt<n)
    {
        int now=q.top().first;
        int dis=q.top().second;
        q.pop();
        if(vis[now]) continue;
        cnt++;
        sum+=dis;
        vis[now]=1;
        for(int i=0; i<e[now].size(); i++)         //可以把这个i声明成register类型 提高效率
        {
            int v=e[now][i].first;
            if(d[v]>e[now][i].second)
            {
                d[v]=e[now][i].second;
                q.push(make_pair(v,d[v]));
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    init(n);
    for(int i=1; i<=m; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,z);
    }
    prim();
    if (cnt==n) printf("%d",sum);
    else printf("orz");
}

 

posted @ 2018-03-21 20:51  yosoro  阅读(559)  评论(0编辑  收藏  举报