最小生成树

来源:互联网 发布:c语言比较三个数大小 编辑:程序博客网 时间:2024/06/05 00:24

注:我在修炼当中,博客完全是为了自己复习方便看得,如果你不慎点入了我的博客,看看就好,不要相信,误人子弟就不好了- -

最小生成树有Prim 和 Kruskal 两种经典算法 ,这两个呢都是利用MST性质来构造最小生成树的算法的,如果忘记了MST,去翻我的博客。

先说说prim算法,有个V点集,从中先选出一个点u0放入U点集中,然后找到一条与u0相连代价最小的边,且该边另一个点v0在V - U点集中。此时将该边加入最小生成树的边的集合中,然后将v0加入到U点集中,之后不断重复该操作,直到所有点都加入到了U点集中。

Prim + 优先队列

不会算时间复杂度,GG,T_T,大概是O(nloge)的,注意优先队列时间复杂度为O(logn)的

#include <iostream>#include <queue>#include<cstring>#include<cstdio>using namespace std;typedef struct{    long v;    long next;    long cost;}Edge;typedef struct{    long v;    long cost;}node;bool operator <(const node &a,const node &b){    return a.cost>b.cost;}priority_queue<node> q;const long MAXN=10000;Edge e[MAXN];long p[MAXN];bool vist[MAXN];long m,n;long from,to,cost;void init(){    memset(p,-1,sizeof(p));    memset(vist,0,sizeof(vist));    while (!q.empty())        q.pop();    long i;    long eid=0;    for (i=0;i<n;++i)    {        scanf("%ld %ld %ld",&from,&to,&cost);        e[eid].next=p[from];        e[eid].v=to;        e[eid].cost=cost;        p[from]=eid++;        //以下适用于无向图        swap(from,to);        e[eid].next=p[from];        e[eid].v=to;        e[eid].cost=cost;        p[from]=eid++;    }}void Prim(){    long cost=0;    init();    node t;    t.v=from;//选择起点    t.cost=0;    q.push(t);    long tt=0;    while (!q.empty()&&tt<m)    {        t=q.top();        q.pop();        if (vist[t.v])        {            continue;        }        cost+=t.cost;        ++tt;        vist[t.v]=true;        long j;        for (j=p[t.v];j!=-1;j=e[j].next)        {            if (!vist[e[j].v])            {                node temp;                temp.v=e[j].v;                temp.cost=e[j].cost;                q.push(temp);            }        }    }    printf("%ld\n",cost);}int main(){    while (scanf("%ld %ld",&m,&n)!=EOF) //m 代表点  n代表边数    {        Prim();    }    return 0;}

Prim

时间复杂度是O(n2),与边数无关,适合求边稠密的网。

int minTree(int n){    int i,j;    memset(v,0,sizeof(v));    v[0]=1;    sum=0;    for(i=1;i<n;i++)    {        min=max;        for(j=0;j<n;j++)        {            if(!v[j]&&map[0][j]<min)            {                min=map[0][j];                flag=j;            }        }        sum+=min;        v[flag]=1;        for(j=0;j<n;j++)        {            if(!v[j]&&map[0][j]>map[flag][j])            {                map[0][j]=map[flag][j];            }        }    }    return sum;}

判断次小生成树

#include <iostream>using namespace std;#define MAX 101#define INF 999999999#define max(a,b) (a>b?a:b)int dis[MAX], pre[MAX];int edge[MAX][MAX];int maxVal[MAX][MAX];bool inTree[MAX][MAX];bool vis[MAX];int Prime ( int n ){    int i, j, k, minc, mst;    for ( i = 1; i <= n; i++ )    {        dis[i] = edge[1][i];        vis[i] = false;        pre[i] = 1;    }    dis[1] = mst = 0;     vis[1] = true;    for ( i = 2; i <= n; i++ )     {        minc = INF; k = -1;        for ( j = 1; j <= n; j++ )        {            if ( ! vis[j] && dis[j] < minc )            {                minc = dis[j];                k = j;            }        }        if ( minc == INF ) return -1;  // 图不连通,没有找到最小生成树        mst += minc;        vis[k] = true;        inTree[pre[k]][k] = inTree[k][pre[k]] = true;  // 记录加入的树中的边        for ( j = 1; j <= n; j++ )            if ( vis[j] == true )                maxVal[j][k] = max ( maxVal[j][pre[k]], edge[pre[k]][k] ); // 找j-k的路径上权值最大的那条边,并记录在maxVal[j][k]中        for ( j = 1; j <= n; j++ )        {            if ( ! vis[j] && dis[j] > edge[k][j] )            {                dis[j] = edge[k][j];                pre[j] = k;   // 修正前驱            }        }    }    return mst;}void initial ( int n ){    for ( int i = 1; i < n; i++ )    {        for ( int j = i + 1; j <= n; j++ )        {            edge[i][j] = edge[j][i] = INF;            inTree[i][j] = inTree[j][i] = 0;            maxVal[i][j] = maxVal[j][i] = 0;        }    }}   int main(){    int t, n, m, u, v, w;    scanf("%d",&t);     while ( t-- )    {        scanf("%d%d",&n,&m);        initial ( n );        while ( m-- )        {            scanf("%d%d%d",&u,&v,&w);            edge[u][v] = edge[v][u] = w;        }        int mst = Prime ( n );        if ( mst < 0 ) { printf("0\n"); continue; }        int res;        bool flag = false;        for ( int i = 1; i < n; i++ )        {            for ( int j = i + 1; j <= n; j++ )            {                if ( inTree[i][j] || edge[i][j] == INF ) continue; // 边edge[i][j]在树中或者i,j之间无边                res = mst + edge[i][j] - maxVal[i][j]; // 用边edge[i][j], 替换i-j路径上权值最大的那条边,得到一棵新的生成树                if ( res == mst ) { flag = true; break; }            }            if ( flag ) break;        }        if ( flag )            printf("Not Unique!\n");        else            printf("%d\n",mst);    }    return 0;}

Kruskal

该算法是初始时所有点自成一个连通分量,然后在所有边中选择一条权重最小的边放入边集中,然后再寻找下一条最小的边,并且将该边加入边集的条件是该边
时间复杂度为O(eloge),适合边稀疏的网

#include<iostream>//这里的n代表节点数,给节点初始化,m代表边数,需要给边数排序,#include<stdio.h>#include<string.h>//kruskal算法主要用的是并查集#include<algorithm>using namespace std;int inf=999999;int fa[110000];int n,m;struct node{    int u,v,w;}q[110000];void make_set(){    for(int i=1;i<=n;i++)    {        fa[i]=i;    }}int Find(int x){    if(x!=fa[x])        fa[x]=Find(fa[x]);        return fa[x];}bool cmp(node x,node y){    return x.w<y.w;}int kru(){  int ans=0,cnt=0;    make_set();    sort(q+1,q+m+1,cmp);//排序要从起始地址,到终止地址此题中i是从1开始的,    for(int i=1;i<=m;i++)    {        int fx=Find(q[i].u);        int fy=Find(q[i].v);        if(fx!=fy)          {           fa[fx]=fy;           ans+=q[i].w;            cnt++;          }    }    return ans;}int main(){    while(scanf("%d%d",&n,&m)!=EOF)    {        for(int i=1;i<=m;i++)        {            scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w);        }        printf("%d\n",kru());    }    return 0;}
0 0