题解P3366 最小生成树

2018-04-22 15:19:36


1.Kruskal

先对所有的边进行排序,取最小的边。把以取的点通过并查集并起来,如果一条边的两个点已经在并查集内,则选这条边会导致树变成图。所以并查集就是用来判断能否加边的。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
struct edge
{
    int x,y,z;
}e[200011];
int f[5010];
bool cmp(edge a,edge b)
{
    return a.z<b.z;
}
int find(int x)
{
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
        cin>>e[i].x>>e[i].y>>e[i].z;
    sort(e+1,e+m+1,cmp);
    int cnt=0,sum=0;
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int u=e[i].x,v=e[i].y;
        if(find(u)==find(v)) continue;
        sum+=e[i].z;
        cnt++;
        f[find(u)]=find(v);
        if(cnt==n-1) break;
    }
    cout<<sum;
    return 0;
}

2.Prim

prim适用于稠密的图。用dis[i]记录i到最小生成树这个集合的最小距离,从一个点开始扩展,不断的将最近的点加进这个集合。加进去之后再通过这个点去更新其他点的dis。类似于dijstra

#include<map> 
#include<vector> 
#include<list> 
#include<cmath> 
#include<cstdio> 
#include<cstring> 
#include<iostream> 
#include<algorithm> 
using namespace std;
int n,m,ans=0,num=1;
int cnt=0;
int f[5010],dis[5010],a[5010][5010];
void prime()
{
    int l=0,r=1,u=1;
    dis[1]=0,f[1]=1;
    while(num<n)
    {
        int minn=0x7fffff;
        num++;
        for(int i=1;i<=n;i++)
        {
            if(dis[i]>a[u][i] && !f[i])
            {
                dis[i]=a[u][i];//更新其他点的距离
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(!f[i] && dis[i]<minn)
            {
                minn=dis[i];
                u=i;//取距离最小的一个点再次更新
            }
        }
        ans+=minn;
        f[u]=1;
    }
}
int main()
{
    memset(dis,0x7f,sizeof(dis));
    memset(a,0x7f,sizeof(a));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(a[x][y]>z) a[x][y]=z,a[y][x]=z;
    }
    prime();
    if(num==n) cout<<ans;
    else cout<<"orz";
    return 0;
} 

加上堆优化(也就是优先队列):

此处引用的是一大佬代码,自己懒得打了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n,m,cnt,sum,ai,bi,ci,head[5005],dis[5005],vis[5005];
struct Edge
{
    int v,w,next;
}e[400005];
void add(int u,int v,int w)
{
    e[++k].v=v;
    e[k].w=w;
    e[k].next=head[u];
    head[u]=k;
}
typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > q;//优先队列!
void prim()
{
    dis[1]=0;
    q.push(make_pair(0,1));
    while(!q.empty()&&cnt<n)
    {
        int d=q.top().first,u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        cnt++;
        sum+=d;
        vis[u]=1;
        for(int i=head[u];i!=-1;i=e[i].next)
            if(e[i].w<dis[e[i].v]) dis[e[i].v]=e[i].w,q.push(make_pair(dis[e[i].v],e[i].v));
    }
}
int main()
{
    memset(dis,0x7f,sizeof(dis));
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&ai,&bi,&ci);
        add(ai,bi,ci);
        add(bi,ai,ci);
    }
    prim();
    if(cnt==n) printf("%d",sum);
    else printf("orz");
}