最小生成树 (学习笔记)

最小生成树 (学习笔记)

什么是最小生成树呢??
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用Kruskal(克鲁斯卡尔)算法Prim(普里姆)算法求出。(源于百度百科

  1. Kruskal算法基本思想:
    假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
  2. Prim算法基本思想:
    在带权连通图中V是包含所有顶点的集合, U已经在最小生成树中的节点,从图中任意某一顶点v开始,此时集合U={v},重复执行下述操作:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边,将(u,w)这条边加入到已找到边的集合,并且将点w加入到集合U中,当U=V时,就找到了这颗最小生成树。

实例

1. HDU 1102 ---- Constructing Roads

Problem Description
There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected.

We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.

Input
The first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j.

Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.

Output
You should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum.

Sample Input

3
0 990 692
990 0 179
692 179 0
1
1 2

Sample Output

179

Kruskal:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

struct dist
{
    int x;
    int y;
    int dis;
} road[105*105]; //方便排序,采用结构体

int parent[1010];

int findroot(int x)
{
    if (parent[x]==x) return parent[x];
    parent[x]=findroot(parent[x]);
    return parent[x];
}

void Merge(int a , int b)
{
    int l=findroot(a);
    int r=findroot(b);
    if (l!=r) parent[l]=r;
}

bool cmp(dist a, dist b)
{
    return a.dis < b.dis;
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        //填数据
        int tmp,cnt=0; //cnt计数器一定要从0开始,不然待会儿排序和取数会受影响
        for(int r=1 ; r<=n ; r++)
        {   //第r行
            for(int c=1 ; c<=n ; c++)
            {   //第c列
                scanf("%d",&tmp);
                road[cnt++]={r,c,tmp};//把从r到c的距离都储存到road数组中
            }
        }
        //算法开始
        //按距离从小到大排序
        sort(road , road+cnt , cmp);
        //并查集初始化
        for(int i=0 ; i<=n ; i++)
            parent[i]=i;

        int q,a,b; scanf("%d",&q);
        for(int i=1 ; i<=q ; i++)
        {
            scanf("%d%d",&a,&b);
            Merge(a,b);
        }
        //运算
        int sum=0; //路的长度
        for(int i=0 ; i<cnt ; i++)
        {
            if(road[i].dis==0 || parent[ road[i].x ]==parent[ road[i].y ])
            {   //跳过距离为0的点 跳过已经连接的两个点
                continue;
            }
            else
            {   //连接操作
                //直接连接这两个点,不是连接父结点Merge(parent[ road[i].x ] , parent[ road[i].y ]);
                //Merge(road[i].x , road[i].y);
                int l=findroot(road[i].x);
                int r=findroot(road[i].y);
                if (l!=r)
                {
                    parent[l]=r;
                    q++;  //路的条数+1
                    sum+=road[i].dis; //需要再修的距离累加
                }
            }
            //if (q==n-1) break; 这句不能加
        }
        printf("%d\n",sum);
    }
    return 0;
}

2. POJ 1251 ---- Jungle Roads

Description

The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads between villages some years ago. But the jungle overtakes roads relentlessly, so the large road network is too expensive to maintain. The Council of Elders must choose to stop maintaining some roads. The map above on the left shows all the roads in use now and the cost in aacms per month to maintain them. Of course there needs to be some way to get between all the villages on maintained roads, even if the route is not as short as before. The Chief Elder would like to tell the Council of Elders what would be the smallest amount they could spend in aacms per month to maintain roads that would connect all the villages. The villages are labeled A through I in the maps above. The map on the right shows the roads that could be maintained most cheaply, for 216 aacms per month. Your task is to write a program that will solve such problems.

Input
The input consists of one to 100 data sets, followed by a final line containing only 0. Each data set starts with a line containing only a number n, which is the number of villages, 1 < n < 27, and the villages are labeled with the first n letters of the alphabet, capitalized. Each data set is completed with n-1 lines that start with village labels in alphabetical order. There is no line for the last village. Each line for a village starts with the village label followed by a number, k, of roads from this village to villages with labels later in the alphabet. If k is greater than 0, the line continues with data for each of the k roads. The data for each road is the village label for the other end of the road followed by the monthly maintenance cost in aacms for the road. Maintenance costs will be positive integers less than 100. All data fields in the row are separated by single blanks. The road network will always allow travel between all the villages. The network will never have more than 75 roads. No village will have more than 15 roads going to other villages (before or after in the alphabet). In the sample input below, the first data set goes with the map above.

Output
The output is one integer per line for each data set: the minimum cost in aacms per month to maintain a road system that connect all the villages. Caution: A brute force solution that examines every possible set of roads will not finish within the one minute time limit.

Sample Input

9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0

Sample Output

216
30

大致题意:在一个公路网中,找出一颗最小树,使得总路径长最小。
输入格式:
村庄个数
村庄A 从A出发的道路个数 村庄B A-B的距离 村庄I A-I的距离
村庄B … …
… …
第n-1个村庄 … …

Prim:

#include <iostream>
#include <string.h>
using namespace std;
//  地图         访问过的点  到最小生成树的距离
int grid[30][30],visited[30],dis_to_tree[30];
int inf = 1000; //inf表示极大值

/* 这道题其实就是求最小生成树 */
int main()
{
    int n; cin>>n;
    while(n!=0)
    {
        //初始化地图
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                grid[i][j] = i == j ? 0 : inf;
            }
        }
        //设置地图
        for(int i=1 ; i<=n-1 ; i++)
        {
            char alphbt;  // 输入字母,代表村庄
            int road;     // 该村庄通向其他村庄路的数量
            cin>>alphbt>>road;
            for(int j=1 ; j<=road ; j++)
            {
                char ch; int cost;
                cin>>ch>>cost;
                grid[alphbt-'A'+1][ch-'A'+1]=cost; //字符转换为数字
                grid[ch-'A'+1][alphbt-'A'+1]=cost; //赋值有两次
            }
        }
        //算法开始----Prim算法
        memset(visited , 0 , sizeof visited);   //初始化visited数组
        int cost_min=0; //最小生成树中所有道路的费用
        visited[1]=1;   //从A村庄开始
        //初始化dis_to_tree
        for(int i=1 ; i<=n ; i++)
        {   //每个点到最小树的距离
            dis_to_tree[i]=grid[i][1];
        }

        //循环n-1次,因为n-1条路足够用了
        for(int i=1 ; i<=n-1 ; i++)
        {
            int minimum=inf,town_num;   //town_num存储--到最小树花费最少的村庄编号
            for(int j=1 ; j<=n ; j++)
            {   //找距离最小树费用最低的一条路
                if(visited[j]==0 && dis_to_tree[j]<minimum)
                {
                    minimum=dis_to_tree[j];
                    town_num=j;
                }
            }
            cost_min+=minimum;
            //更新以下两个值
            dis_to_tree[town_num]=0;
            visited[town_num]=1;
            //更新dis_to_tree
            for(int j=1 ; j<=n ; j++)
            {   //新的村庄加入最小树,更新一一下最小值
                if(visited[j]==0 && grid[j][town_num]<dis_to_tree[j])
                    dis_to_tree[j]=grid[j][town_num];
            }
        }
        cout<<cost_min<<endl;
        cin>>n;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值