最小生成树——prim以及Kruskal
最小生成树——prim以及Kruskal
n点m边
1关于prim算法/适用于稠密图
2关于Kruskal算法/适用于稀疏图 m 远小于n的平方的图称为稀疏图
prim原理:
对树中的点进行遍历,存点构成一个新图,每次找离新图最近的点加入新图。
- 每次加入点时更新可加入边的数量以及大小(将新加入的点带来的边视为可加入)
- 每次全遍历所有可以加入的边找最近点
prim代码实现解释
- 将起始点的一系列临边的点赋值
 for(int i=head[1];i;i=a[i].next)
    {
        int k=a[i].to;
        vi[k]=min(vi[k],a[i].w); //注意有重边   
    }
- 在所有点中找离当前图最近的点,并将最近点标记为now
 for(int i=1;i<=n;i++)
        {
            if(!ans[i]&&ma>vi[i])
            {
                 now=i;
                 ma=vi[i];
            }
        }
3.对now(新加入的点)的临边进行赋值
 for(int i=head[now];i;i=a[i].next)
        {
            int k=a[i].to;
            if(!ans[k]&&vi[k]>a[i].w)
            {
                vi[k]=a[i].w;
            }
        }
prim板子
#include<iostream>
#include<algorithm>
using namespace std;
int head[200005];
int cnt;
int b[200005];
int ans[200005];
int vi[200005];
struct node{
    int next;
    int to;
    int w;
}a[400005];
void add(int x,int y,int w)
{
    a[++cnt].next=head[x];
    a[cnt].to=y;
    a[cnt].w=w;
    head[x]=cnt;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    for(int i=2;i<=n;i++)
    {
        vi[i]=100000000;
    }
    for(int i=head[1];i;i=a[i].next)
    {
        int k=a[i].to;
        vi[k]=min(vi[k],a[i].w); //注意有重边   
    }
   int now=1;
    int r=0;
    int sum=0;
    while(++r<n)
    {
        int ma=100000000;
        ans[now]=1;
        for(int i=1;i<=n;i++)
        {
            if(!ans[i]&&ma>vi[i])
            {
                 now=i;
                 ma=vi[i];
            }
        }
        if(ma==100000000)
        {
            cout<<"orz";
            return 0;
        }
        sum+=ma;
        for(int i=head[now];i;i=a[i].next)
        {
            int k=a[i].to;
            if(!ans[k]&&vi[k]>a[i].w)
            {
                vi[k]=a[i].w;
            }
        }
    }
    cout<<sum;
    return 0;
}
Kruskal原理:
对图的边进行存储,将边权值排序,然后从小到大将边加入新图中
- 用并查集维护。
Kruskal代码实现解释
- 从小到大遍历边,对于每个边进行find判断(并查集内容)
  for(int i=1;i<=m;i++)
    {
        int x=find(a[i].x);
        int y=find(a[i].to);
        if(x!=y)
        {
            sum+=a[i].w;
            fa[x]=y;
            if(++ans==n-1)
            {
                break;
            }
        }
    }
Kruskal板子
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
    int to;
    int x;
    int w;
}a[200005];
int fa[20005];
bool cmp(node x,node y)
{
    return x.w<y.w;
}
int find(int x)
{
    while(x!=fa[x])
    {
        x=fa[x]=fa[fa[x]];
    }
    return x;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>a[i].x>>a[i].to>>a[i].w;
    }
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
    }
    sort(a+1,a+1+m,cmp);
    int sum=0;
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int x=find(a[i].x);
        int y=find(a[i].to);
        if(x!=y)
        {
            sum+=a[i].w;
            fa[x]=y;
            if(++ans==n-1)
            {
                break;
            }
        }
    }
    if(ans==n-1)
    cout<<sum;
    else
    cout<<"orz";
    return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号