最小生成树

12 篇文章 0 订阅

                                         最小生成树的入门

最小生成树就是能将所有点都连起来的最短路的和(我的理解);


看看实例:

有n个城市,给你m条道路;
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2

求出将所有点都连起来的道路总和的最小值;

(1)第一种方法(Kruskal)

利用并查集的思想(不会的先看看并查集)

看代码(有注释)

#include<stdio.h>

struct edge
{
    int u,v,w;//u代表一个端点,v代表另一个端点,w代表权值;
}e[10];//用结构体记录每条路的端点和权值;
int n,m;
int f[7]={0},sum=0,coun=0;//数组f记录每个点的祖宗是谁,sum记录路的长度,coun记录有多少个点在生成树中;

//快排,将边的权值从小到大排序;
void quicksort(int left,int right)
{
    int i,j;
    struct edge t;
    if(left>right)
        return ;
    i=left;
    j=right;
    while(i!=j)
    {
        while(e[j].w>=e[left].w&&i<j)
            j--;
        while(e[i].w<=e[left].w&&i<j)
            i++;
        if(i<j)
        {
            t=e[i];
            e[i]=e[j];
            e[j]=t;
        }
    }
    t=e[left];
    e[left]=e[i];
    e[i]=t;
    quicksort(left,i-1);
    quicksort(i+1,right);
    return ;
}

//getf是寻找这个点的祖宗是谁,并更新每个点的祖宗;
int getf(int v)
{
    if(f[v]==v)
        return v;
    else
    {
        f[v]=getf(f[v]);
        return f[v];
    }
}

//查看v,u两点是否为一个祖宗,如果不是,就改为一个祖宗;
int merge(int v,int u)
{
    int t1=getf(v);
    int t2=getf(u);
    if(t1!=t2)
    {
        f[t2]=t1;
        return 1;
    }
    return 0;
}

int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)//存储每条边的信息;
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    quicksort(1,m);//对每条边进行排序,按权值从小到大排;
    for(i=1;i<=n;i++)//数组初始化,刚开始每个点的祖宗都是自己;
        f[i]=i;
    for(i=1;i<=m;i++)//遍历每条边;
    {
        //查看是否为一个祖宗;
        //如果是一个祖宗,说明已经进入一个生成树中,就不需要再加了;
        //如果不是一个祖宗,就让他进入生成树,并加上他的权值;
        if(merge(e[i].u,e[i].v))
        {
            coun++;//有一个新的点进入生成树;
            sum=sum+e[i].w;//加上权值;
        }
        if(coun==n-1)//如果此时所有的点已经都进入了生成树中,说明已经经过了所有的点;
            break;
    }
    printf("%d\n",sum);//输出最小的和;
    return 0;
}

(2)第二种方法(Prim)

就是用最短路的Dijkstra的方法找最短的边;(时间复杂度较高)

#include<stdio.h>
#define inf 0x3f3f3f3f
int n,m;
int map[10][10];//跟最短路一样,存储边的信息,记住是无向图;
int dis[10],xia[10];
//非常重要:dis是记录最短的边长(从一个点到这个点的边长,而不是从起点到这点的最短路径);
//xia是记录那个点已经连接到了;
int main()
{
    int i,j;
    while(~scanf("%d%d",&n,&m))
    {
        int sum=0,count=0;//sum记录最短的路程,count记录有几个点被链接了;
        for(i=1;i<=n;i++)//初始化;
        {
            for(j=1;j<=n;j++)
            {
               if(i==j) map[i][j]=0;
               else map[i][j]=inf;
            }
        }
        for(i=1;i<=m;i++)//存储边的信息,与最短路的存储方式一样;
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            map[a][b]=map[b][a]=c;//无向图;
        }
        for(i=1;i<=n;i++)//初始化;
        {
            dis[i]=map[1][i];
            xia[i]=0;
        }
        xia[1]=1;//因为初始化时,已经将与1相连的边的信息存入了dis中;
        count++;
        int min,x;
        while(count<n)
        {
            min=inf;
            for(i=1;i<=n;i++)//找出最短边的另一个顶点,用x记录;
                if(xia[i]==0&&dis[i]<min)
                    min=dis[x=i];
            xia[x]=1;//标记一下;
            count++;
            sum+=dis[x];
            for(i=1;i<=n;i++)//将与x相连的点的边长存入dis中(以前存的是最短路,此时存的是边长);
                if(xia[i]==0&&dis[i]>map[x][i])//这条边必须是两顶点没有被标记过的,标记过的不算;
                    dis[i]=map[x][i];
        }
        printf("%d\n",sum);
    }
    return 0;
}

(3)按照自己的理解写的(供自己看)

#include<stdio.h>
#include<algorithm>
using namespace std;
#include<queue>
int n,m;

struct node
{
    int s,e,w;
    friend operator <(node n1,node n2)
    {
        return n1.w>n2.w;
    }
} edge[100];

int main()
{
    int i,j;
    while(~scanf("%d%d",&n,&m))
    {
        priority_queue<node>q;
        for(i=1; i<=m; i++)
        {
            scanf("%d%d%d",&edge[i].s,&edge[i].e,&edge[i].w);
            if(edge[i].s==1||edge[i].e==1)
                q.push(edge[i]);
        }
        int sum=0,countt=0;
        node n1;
        int xia[100]= {0};
        xia[1]=1;
        while(!q.empty())
        {
            n1=q.top();
            if(countt==n)
                break;
            q.pop();
            if(xia[n1.s]==0||xia[n1.e]==0)
            {
                sum+=n1.w;
                countt++;
                int x;
                if(xia[n1.s]==0)
                    xia[x=n1.s]=1;
                else
                    xia[x=n1.e]=1;
                for(i=1; i<=m; i++)
                {
                    if(edge[i].s==x||edge[i].e==x)
                        q.push(edge[i]);
                }
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值