zoukankan      html  css  js  c++  java
  • 算法竞赛模板 最小生成树

    ①克鲁斯卡尔算法(Kruskal)

    n是顶点数,m是边数;

    cnt是已连的边数,如果已连的边数=总点数-1,即可跳出。

    #include<bits/stdc++.h>
    #define MAX 1005
    using namespace std; 
    int p[MAX],n,m;
    struct edge{
        int x,y,w;
    }a[MAX]; 
    int find(int r)
    {
        if(p[r]!=r)
            p[r]=find(p[r]);
        return p[r];
    }
    void join(int x,int y)
    {
        int fx=find(x),fy=find(y);
        if(fx!=fy)
            p[fx]=fy;
    }
    void init()
    {
        for(int i=0;i<=MAX;i++)
            p[i]=i;
    }
    bool cmp(edge a,edge b)
    {
        return a.w<b.w;
    }
    int kruskal()
    {
        sort(a,a+m,cmp);
        int cnt=0,cost=0,i;
        for(i=0;i<m;i++)
        {
            int fx=find(a[i].x),fy=find(a[i].y);
            if(fx!=fy)
            {
                p[fx]=fy;
                cost+=a[i].w;
                cnt++;
            } 
            if(cnt==n-1)break;
        }
        return cost;
    }
    int main()
    {
        int d,i;
        while(cin>>n>>m)
        {
            init();
            for(i=0;i<m;i++)
            {
                scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].w,&d);
                if(d==1)//将已有的路连起来 
                    join(a[i].x,a[i].y);
            }
            cout<<kruskal()<<endl;    
        }
        return 0;
    }

    ②普里姆算法(Prim)

    (1) n是顶点数,m是边数;

    (2) edge[i][j]表示边e=(i,j)的权值,不存在的情况下设为INF,所以要记得提前做赋值操作;

    (3) mincost[i]数组表示已访问点的集合每个未访问点 i 的最小权值;

    (4) vis[i]表示顶点 i 是否已访问

    <1> 基础prim算法

    #include<bits/stdc++.h>
    #define MAX 1005
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,edge[MAX][MAX],mincost[MAX],vis[MAX];
    void init()
    {
        int u,v,w,i;
        memset(edge,INF,sizeof(edge));
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            edge[u][v]=edge[v][u]=w;
        }
        for(i=1;i<=n;i++)
        {
            vis[i]=0;
            mincost[i]=INF;
        }
        mincost[1]=0;
    }
    int prim()
    {
        int i,v,cost=0;
        init();
        while(1)
        {
            v=-1;
            for(i=1;i<=n;i++)
                if(!vis[i]&&(v==-1||mincost[i]<mincost[v]))
                    v=i;
            if(v==-1)
                break;
            vis[v]=1;
            cost+=mincost[v];
            for(i=1;i<=n;i++)
                mincost[i]=min(mincost[i],edge[i][v]);
        }
        return cost;
    }
    int main()
    {
        cin>>n>>m;
        cout<<prim()<<endl;
        return 0;
    }

    <2> prim之记录并输出每次加入的边

    #include<bits/stdc++.h>
    #define MAX 1005
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,edge[MAX][MAX],mincost[MAX],vis[MAX];
    void init()
    {
        int u,v,w,i;
        memset(edge,INF,sizeof(edge));
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            edge[u][v]=edge[v][u]=w;
        }
        for(i=1;i<=n;i++)
        {
            vis[i]=0;
            mincost[i]=INF;
        }
        mincost[1]=0;
    }
    void prim()
    {
        int i,v,start,end,sum=0,minn;
        init();
        while(1)
        {
            v=-1;
            for(i=1;i<=n;i++)
                if(!vis[i]&&(v==-1||mincost[i]<mincost[v]))
                    v=i;
    
            if(v==-1)
                break;
    
            vis[v]=1,sum++;
            minn=INF;
            for(i=1;i<=n;i++)
            {
                mincost[i]=min(mincost[i],edge[i][v]);
                if(minn>mincost[i]&&!vis[i])
                {
                    minn=mincost[i];
                    end=i;
                }
            }
            if(sum==n)
                continue;
    
            minn=INF;
            for(i=n;i>=1;i--)
            {
                if(minn>=edge[i][end]&&vis[i])
                {
                    minn=edge[i][end];
                    start=i;
                }
            }
            printf("%d %d %d
    ",start,end,mincost[end]);
        }
    }
    int main()
    {
        cin>>n>>m;
        prim();
        return 0;
    }
    #include<bits/stdc++.h>
    #define MAX 1005
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,edge[MAX][MAX],mincost[MAX],vis[MAX];
    void init()
    {
        int u,v,w,i;
        memset(edge,INF,sizeof(edge));
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            edge[u][v]=edge[v][u]=w;
        }
        for(i=1;i<=n;i++)
        {
            vis[i]=0;
            mincost[i]=INF;
        }
        mincost[1]=0;   //必须先确保下标的这个点1是存在的!
    }
    int prim()
    {
        int i,v,cost=0;
        init();
        while(1)
        {
            /*--------------------循环A---------------------*/
            /*
                PS:点v是距已访问点集合最近的未访问点
    
                1、若点i是进循环A以来,第一个未被访问过的点,则无条件保存到点v
                2、若点i不是第一个未被访问过的点,但点i距离已访问点集合更近,则保存到点v
            */
            v=-1;
            for(i=1;i<=n;i++)
                if(!vis[i]&&(v==-1||mincost[i]<mincost[v]))
                    v=i;
    
            /*--------------------循环A---------------------*/
    
    
            //如果所有点都被访问过了,v还是-1,则会跳出这个while循环
            if(v==-1)
                break;
    
            //将访问到的最近点v标记为已访问状态
            vis[v]=1;
    
            //总距离增加
            cost+=mincost[v];
    
    
            /*--------------------循环B---------------------*/
            /*
                PS:mincost[i]是已访问点集合到每个未访问点i的最小权值
    
                1、若新增的点v距离某个未访问点i相较于之前更近,则将 已访问点集合到i的距离 更新为edge[v][i]
                2、否则保持原样
            */
            for(i=1;i<=n;i++)
                mincost[i]=min(mincost[i],edge[v][i]);
    
            /*--------------------循环B---------------------*/
        }
        return cost;
    }
    int main()
    {
        cin>>n>>m;
        cout<<prim()<<endl;
        return 0;
    }
    prim算法详解!不理解就ClickHere!!!!

    ③ 总结

    Kruskal在所有边中不断寻找最小的边,Prim在U和V两个集合之间寻找权值最小的连接。

    它们的共同点在于:构造过程中都不能形成环!

    (1) 时间上:

      Prim适合稠密图,复杂度为O(n*n),因此通常使用邻接矩阵储存;

      Kruskal适合稀疏图,多用邻接表储存,复杂度为O(e*loge)。

    (2) 空间上:

      Prim适合边较多的情况;

      Kruskal适合点较多的情况。

  • 相关阅读:
    DSP EPWM学习笔记2
    DSP EPWM学习笔记1
    DSP
    DSP bootloader学习笔记1
    Source Insight 中使用 AStyle 代码格式工具
    DSP基础学习-ADC同步采样
    DSP算法学习-过采样技术
    救救一个只会用万能头的孩子!
    传参(转)
    return
  • 原文地址:https://www.cnblogs.com/kannyi/p/8587733.html
Copyright © 2011-2022 走看看