最小生成树

    在一个具有几个顶点的连通图G中,如果存在子图G'包含G中所有顶点和一部分边,且不形成回路,则称G'为图G的生成树,代价最小生成树则称为最小生成树。

最小生成树的特点:

(1)MST性质  最小生成树性质:设G=(V,E)是一个连通网络,U是顶点集V的一个真子集。若(u,v)是G中一条“一个端点在U中(例如:u∈U),另一个端点不在U中的边(例如:v∈V-U),且(u,v)具有最小权值,则一定存在G的一棵最小生成树包括此边(u,v)。

算法1:Prime算法,复杂度为o(n^2),与网中的边数无关,所以适合于求边稠密的网的最小生成树。它的算法思想相当于贪心算法,首先从任意顶点Vi开始构造生成树,并将Vi加入到生成树T中,用一个一维数组dist[j]记录其他顶点到Vi的距离,扫描一维数组dist[j],选取值最小的顶点j和相应的边加入T中,再以j为中间点更新Vj顶点到其它的顶点k的距离值,如果dist[k]>map[j][k](map[j][k]为顶点j到k的权值),则更新dist[k]为map[j][k],重复这个操作直到T中有n个顶点。

在构造邻接矩阵的时候如果两个边没有相连的话,直接设这个值为无穷大~~

例题1:NYOJ 38(布线问题),分析:Prime算法,最小与外界相连的费用的楼作为起始点求最小生成树。以这题来具体分析Prime算法的步骤。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX=505;
#define Inf 10000000
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int num,n,m,cost[MAX],Dist[MAX],visit[MAX],map[MAX][MAX];
int Prime(int pos)
{   int sum=0;//记录最小生成树的权值 
    CLR(visit,0);//访问初始化,没有访问的初始化为0 
    for(int i=1;i<=n;i++) //记录其它顶点到点pos的距离 
        Dist[i]=map[pos][i];
    visit[pos]=1;
    for(int i=1;i<n;i++)//寻找其它的n-1条边
    {   int min=Inf;//记录最小的边 
        for(int j=1;j<=n;j++)
            if(!visit[j]&&min>Dist[j]) min=Dist[j],pos=j;
        sum+=min;
        visit[pos]=1;
        for(int j=1;j<=n;j++) //如果当前的这个点的邻边比原来Dist[]数组记录的小的话,更新
            if(!visit[j]&&Dist[j]>map[pos][j]) Dist[j]=map[pos][j];        
    }     
    return sum;
}
int main()
{   scanf("%d",&num);
    while(num--)
    {   for(int i=0;i<MAX;i++)
            fill(map[i],map[i]+MAX,Inf);
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {   int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            map[u][v]=map[v][u]=w;
        } 
        for(int i=1;i<=n;i++)
            scanf("%d",&cost[i]);
        int pos=1,min=cost[1];
        for(int i=1;i<=n;i++)
            if(min>cost[i]) min=cost[i],pos=i;
        printf("%d\n",Prime(pos)+min);        
    }
    return 0;
}

算法2:Kruskal算法,过程为:先将图中的各边按照从小到大的顺序排列,然后遍历该图,找到权值最小的边的顶点,该顶点不能访问过,且不能和以前的边组成回路,这个时候可以用并查集(并查集的知识可以点这里)来判连通,如果满足这两个条件则将该条边加入到最小生成树T中,循环,直到找到n-1条边为止。由于这个算法只与边有关,所以适合于稀疏图。开始看错了,这个行数e要求的是n*(n-1)/2,我一直定义的是505,RE了n次,还是得看清楚题目,代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX=125000;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int num,n,m,cost[MAX];
struct Graph{
    int a,b,w;
    friend bool operator<(const Graph &G1,const Graph &G2)
    {   return G1.w<G2.w; 
    } 
}G[MAX];
class UnionFindSet{
public:
    void Init()
    {   CLR(father,-1);
        fill(rank,rank+MAX,1);
    }   
    int Find(int u)
    {   return father[u]==-1?u:Find(father[u]);
    }
    bool Union(int u,int v)
    {   u=Find(u);
        v=Find(v);
        if(u==v) return false;
        if(rank[u]>rank[v])
        {   rank[u]+=rank[v];
            father[v]=u;
        }
        else
        {   rank[v]+=rank[u];
            father[u]=v;
        }
        return true;
    }
private:
    int father[MAX],rank[MAX];    
}UFS;
int main()
{   scanf("%d",&num);
    while(num--)
    {   UFS.Init();
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
            scanf("%d%d%d",&G[i].a,&G[i].b,&G[i].w);
        sort(G,G+m); 
        int sum=0,count=0;
        for(int i=0;i<m;i++)
        {   if(UFS.Union(G[i].a,G[i].b))
            {   sum+=G[i].w; 
                count++;
            }
            if(count==n-1) break;
        }
        for(int i=0;i<n;i++)
            scanf("%d",&cost[i]);   
        printf("%d\n",*min_element(cost,cost+n)+sum);            
    }
    return 0;
}

算法3:邻接表+优先队列做法,有关邻接表的模板可以见这里,这个模板是转载自张云聪大神的~,很好用!!直接贴标准程序的代码:

#include<queue> 
#include<iostream> 
#include<cstring> 
#include<cstdio> 
#include<numeric> 
#include<algorithm> 
using namespace std; 
#define CLR(arr,val) memset(arr,val,sizeof(arr)) 
struct Node 
{   Node(){} 
    Node(int num,int len):len(len),num(num){} 
    int len,num; 
}; 
bool operator<(const Node& n1,const Node& n2) 
{   return n1.len>n2.len; 
} 
const int MAX=510; 
const int MAXE=250000; 
int Head[MAX],Next[MAXE],Num[MAXE],Len[MAXE]; 
int Dis[MAX],top; 
void add(int u,int v,int len) 
{   Num[top]=v; 
    Next[top]=Head[u]; 
    Len[top]=len; 
    Head[u]=top++; 
} 
bool InQ[MAX]; 
int main() 
{   priority_queue<Node> q; 
    int t,m,n,a,b,l; 
    scanf("%d",&t); 
    while(t--)
    {   top=0;CLR(Head,-1); 
        CLR(Dis,0x3f); 
        scanf("%d%d",&m,&n); 
        for(int i=0;i!=n;i++)
        {   scanf("%d%d%d",&a,&b,&l);
            add(a-1,b-1,l);add(b-1,a-1,l); 
        } 
        Dis[0]=0;
        q.push(Node(0,0)); 
        while(!q.empty()) 
        {   Node t=q.top();q.pop(); 
            if(Dis[t.num]!=t.len) continue; 
            for(int i=Head[t.num];i!=-1;i=Next[i])
            {   if(Dis[Num[i]]>Len[i]) 
                {   Dis[Num[i]]=Len[i]; 
                    q.push(Node(Num[i],Len[i])); 
                } 
            } 
        } 
        int minl=0x3f3f3f3f; 
        for(int i=0;i!=m;i++) 
        {   scanf("%d",&l); 
            minl=min(minl,l); 
        } 
        printf("%d\n",accumulate(Dis,Dis+m,0)+minl); 
    } 
    return 0;
}

Prime算法题目总结:(有待更新)

nyist 38,434;

hdu 1102,1162,1233,1863,1301,1875

poj 1789,2253,2349,1251,1861,2421,2728,2485,1287。。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值