最小生成树及其解决算法

有n个城市,每个城市距离不等 ,需要把他们连起来,要求从一个 城市可以走到所有其他的城市,且路的长度要求最少(权值最小)。

                       这个时候,就需要构造最小生成树了。(n个城市需要n-1条边)。 我们在这里介绍两种算法,每种算法都有自身的优势,我们放在最后讨论。


解决方案

(—)Kruskal算法

构成生成树的准则有三条:

<1> 必须只使用该网络中的边来构造最小生成树。

<2> 必须使用且仅使用n-1条边来连接网络中的n个顶点

<3> 不能使用产生回路的边。

 step:   

              <1>      有n个城市,则用数组point[n]来表示, 

                            n个城市最多有 n*(n-1)/2 条边, 所以需要创建 n*(n-1)/2个这样的结构体。

struct edge
{
        int begin;  //这条边 左边的城市编号
        int weight; //这条边的长度
        int end;    //这条边 右边的城市编号
}set[n*(n-1)/2];

                             并输入数据

 for(int i = 0; i < n*(n-1)/2; ++i)
                        scanf("%d%d%d", &set[i].begin, &set[i].end, &set[i].weight);

              <2>    用sort 给 这些的结构体按 边的长度 从小到大 排序

 sort(set, set+tmp, cmp );

              <3> 遍历这些结构体,用并查集判断每个结构体中的 左边城市 和 右边城市的根 是否相同

                         不同,  则合并左城市与右城市,并且,ans += 这条边的长度

                         相同,  什么也不做(不合并,因为最小生成树,已经连通的城市没必要再浪费金钱去联通)。  

init();   //初始化并查集
        for(int i = 0; i < n*(n-1)/2; ++i)
                if( find(set[i].begin) != find(set[i].end))
                {
                        Union(set[i].begin, set[i].end);
                        ans += set[i].weight;
                }

        return ans;
            <4>   如果已经达到了 n-1条边,那么最小生成树一定已经完成。所以可以这样剪 枝
 init();   //初始化并查集  
        int count = 0;
        for(int i = 0; i < n*(n-1)/2 && count != n-1; ++i)
                if( find(set[i].begin) != find(set[i].end))
                {
                        Union(set[i].begin, set[i].end);
                        ans += set[i].weight;
                        count++;                                
                }

        return ans;

完整代码如下:
#include <algorithm>
#include <cstdio>
#define max 10005
using namespace std;
int n, tmp;
int point[max];//用来表示每个点,以供并查集操作

struct edge
{
        int begin;
        int weight;
        int end;
}set[max];

bool cmp(const edge& a, const edge& b)
{
        return a.weight < b.weight;
}

int find(const int& index)
{
        return point[index] = point[index] == index?index:find(point[index]);
}

void Union(const int& a,const int& b)
{
        point[find(a)] = find(b);
}

void init()
{
        for(int i = 0; i <= n; ++i) //这里,可是 了我一次又一次啊,必须是 i= 0到i<=n,而不是i=0到i < n;
                point[i] = i;
}


int krustal()
{
        int ans = 0;
        sort(set, set+tmp, cmp );
        init();   //初始化并查集
        for(int i = 0; i < tmp; ++i)
                if( find(set[i].begin) != find(set[i].end))
                {
                        Union(set[i].begin, set[i].end);
                        ans += set[i].weight;//这个版本为未剪枝的版本
                }

        return ans;
}

int main()
{
        while( ~scanf("%d", &n) && n )
        {
                tmp = n*(n-1)/2;
               
         for(int i = 0; i < tmp; ++i)
             scanf("%d%d%d", &set[i].begin, &set[i].end, &set[i].weight);
printf("%d\n", krustal() ); } return 0;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值