图论——最小生成树(Prim算法,Kruskal算法及常用模板)

    • 最小生成树算法——Prim 算法(普⾥姆)
    • Prim代码模板
    • 例题 POJ - 1258 Agri-Net
    • AC代码
    • 最小生成树算法——Kruskal 算法(克鲁斯卡尔)
    • Kruskal代码模板
    • 例题 POJ - 1287 Networking
    • AC代码

最小生成树算法——Prim 算法(普⾥姆)

从某⼀个顶点开始构建⽣成树;每次将代价最⼩的新顶点纳⼊⽣成树,直到所有顶点都纳⼊为⽌。

算法描述:

  1. 在一个加权连通图中,顶点集合C,边集合为E
  2. 任意选出一个点作为初始顶点,标记为✔,计算所有与之相连接的点的距离,选择距离最短的,标记✔.
  3. 重复以下操作,直到所有点都被标记为✔:
    在剩下的点中,计算与已标记✔点距离最小的点,标记✔,证明加入了最小生成树。

图论——最小生成树(Prim算法,Kruskal算法及常用模板)_第1张图片
最小生成树构造过程如下图:
图论——最小生成树(Prim算法,Kruskal算法及常用模板)_第2张图片

最小生成树生成的树可能不同,但是最小的权值肯定是一样的并且是最小的。
通过上小两个图可以看出图不同但是权值相同,权值相同都是15;

另一种构造方式如下图
图论——最小生成树(Prim算法,Kruskal算法及常用模板)_第3张图片
时间复杂度:O(n^2) 适合⽤于边稠密图

Prim代码模板

int  prim(int  k){//从顶点k开始构建最小生成树 
	memset(book,0,sizeof(book));//初始化标记数组 
	count=0;//初始化已经标记的点个数 
	sum=0;//初始化权重
	for(i=1;i<=n;i++){//初始化所有顶点
		dis[i]=e[k][i];//其余顶点与顶点k的最小距离 
	}
	book[1]=1;//标记k点 
	count++;//更新点个数
	while(count<n){//循环遍历各个点 
		min=inf;
		for(i=1;i<=n;i++){//遍历所有点
			if(book[i]==0&&dis[i]<min){//找到离树的最小权重并且没有被标记的点 
				min=dis[i]; 
				j=i;//找到离树的最小权重的下标 
			}
		}
		book[j]=1;//标记当前点 
		count++;更新点个数
		sum+=dis[j];加上权重
		for(k=1;k<=n;k++){//遍历所有点更新到树的最小权重
			if(book[k]==0&&dis[k]>e[j][k]){//若有更小的权重
				dis[k]=e[j][k];//更新dis 
			}	
	}
	return  sum; 
}

例题 POJ - 1258 Agri-Net

传送门
Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet connectivity to all farms in the area. He needs your help, of course.
Farmer John ordered a high speed connection for his farm and is going to share his connectivity with the other farmers. To minimize cost, he wants to lay the minimum amount of optical fiber to connect his farm to all the other farms.
Given a list of how much fiber it takes to connect each pair of farms, you must find the minimum amount of fiber needed to connect them all together. Each farm must connect to some other farm such that a packet can flow from any one farm to any other farm.
The distance between any two farms will not exceed 100,000.
Input
The input includes several cases. For each case, the first line contains the number of farms, N (3 <= N <= 100). The following lines contain the N x N conectivity matrix, where each element shows the distance from on farm to another. Logically, they are N lines of N space-separated integers. Physically, they are limited in length to 80 characters, so some lines continue onto others. Of course, the diagonal will be 0, since the distance from farm i to itself is not interesting for this problem.
Output
For each case, output a single integer length that is the sum of the minimum length of fiber required to connect the entire set of farms.
Sample Input
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
Sample Output
28
题意描述:
N个农场,要通过光纤互相连接,求最小成本。N*N代表成本情况,如21代表1->4花费21,17代表2->4花费17.。。。
解题思路:
最小生成树模板题

AC代码

#include
#include 
int n,m,i,j,min,k,t;
int e[1005][1005],dis[10005],book[100000];
int inf=99999999;
int count=0,sum=0;
void  prim(){
	memset(book,0,sizeof(book));
	count=0,sum=0;
	for(i=1;i<=n;i++){
		dis[i]=e[1][i];
	}
	book[1]=1;
	count++;
	while(count<n){
		min=inf;
		for(i=1;i<=n;i++){
			if(book[i]==0&&dis[i]<min){
				min=dis[i];
				j=i;
			}
			
		}
		book[j]=1;
		count++;
		sum+=dis[j];
		for(k=1;k<=n;k++){
			if(book[k]==0&&dis[k]>e[j][k])
				dis[k]=e[j][k];
			}	
	}
}
int main(){

	while(scanf("%d",&n)!=EOF){
		for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			e[i][j]=inf;
		}
	}
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++){
			scanf("%d",&e[i][j]); 
		} 
	prim();
	printf("%d\n",sum);
	}
	return 0;
 } 

最小生成树算法——Kruskal 算法(克鲁斯卡尔)

每次选择⼀条权值最⼩的边,使这 条边的两头连通(原本已经连通的就不选)
直到所有结点都连通

算法基本思想:

Kruakal算法的基本思想是每次都选取不会形成环的权值最小的边,但与Prim不同的是,Kruskal算法引入了连通分量来判断是否形成环。所以难点在于如何判断两个顶点是否属于同一连通分量。我们可以用并查集的思想,直接将同一连通分量的两个顶点的根 father[] 连在一起。也就是说当新的一条边加入时,判断它的两个顶点是否属于一个连通分量,即判断他们的根father[]是否相同。

遍历排序好的边集V进行如下选取,直到选取边数为n-1时

步骤1:选取未选取过的权值最小的边E(i, j)
步骤2:如果顶点i和j位于两个不同的连通分量,则选取边E(i,j)加入边集S,同时将边(i, j)的连通分量连在在一起。
步骤3:继续执行步骤12,直到选取边数为n-1时生成最小生成树

图论——最小生成树(Prim算法,Kruskal算法及常用模板)_第4张图片

时间复杂度:O( |E|log2|E| ) 适合⽤于边稀疏图

Kruskal代码模板

int Kruskal()
{	    
        sort(t+1, t+m+1,cmp);//排序
        for(遍历所有顶点) S[i]=i;//并查集初始化自身为一个集合 
        int total=0;//所选取的边数 
        int sum=0;//权重和 
        for(遍历排序后的所有边){
        	if(total==n-1) break; //直到选取了n-1条边后跳出循环 
            int x=edge[i].u,y=edge[i].v;
            if(find(x)!=find(y)){//若不是同一集合也就是判断是否有环
                sum+=edge[i].w;//选取这条边
                merge(x,y);//加入同一集合 
                total++;
            }
        }
        return sum;//返回权重和 
}

例题 POJ - 1287 Networking

传送门
You are assigned to design network connections between certain points in a wide area. You are given a set of points in the area, and a set of possible routes for the cables that may connect pairs of points. For each possible route between two points, you are given the length of the cable that is needed to connect the points over that route. Note that there may exist many possible routes between two given points. It is assumed that the given possible routes connect (directly or indirectly) each two points in the area.
Your task is to design the network for the area, so that there is a connection (direct or indirect) between every two points (i.e., all the points are interconnected, but not necessarily by a direct cable), and that the total length of the used cable is minimal.
Input
The input file consists of a number of data sets. Each data set defines one required network. The first line of the set contains two integers: the first defines the number P of the given points, and the second the number R of given routes between the points. The following R lines define the given routes between the points, each giving three integer numbers: the first two numbers identify the points, and the third gives the length of the route. The numbers are separated with white spaces. A data set giving only one number P=0 denotes the end of the input. The data sets are separated with an empty line.
The maximal number of points is 50. The maximal length of a given route is 100. The number of possible routes is unlimited. The nodes are identified with integers between 1 and P (inclusive). The routes between two points i and j may be given as i j or as j i.
Output
For each data set, print one number on a separate line that gives the total length of the cable used for the entire designed network.
Sample Input
1 0

2 3
1 2 37
2 1 17
1 2 68

3 7
1 2 19
2 3 11
3 1 7
1 3 5
2 3 89
3 1 91
1 2 32

5 7
1 2 5
2 3 7
2 4 8
4 5 11
3 5 10
1 5 6
4 2 12

0
Sample Output
0
17
16
26
题意描述:
题意 :给定两个点及其边权值,求出各点权值和最小的值(即生成最小生成树)

解题方法:最小生成树

这道题是一棵裸的最小二叉树,即克鲁斯卡尔算法(kruskal),算法大概内容是,首先对所有的边权值进行排序,并判断两个顶点是否连通,判断方法可借鉴并查集思想,将所有的点放入并查集中,判断两个顶点是否连通,即判断顶点是否属于一个集合内,以提升算法效率。可以用邻接矩阵存储边权值,不过在相同点之间存在多个权值时,需要取最小的权。

AC代码

#include
#include 
using namespace std;
struct  edge
{
	int u;
	int v;
	int w;
};
struct edge e[1000010],t;
int n,m;
int f[100010]={0},sum=0,count=0;
//sort排序cmp 
bool cmp(edge a,edge b)
{
	return a.w<b.w;
}
//并查集 
int getf(int v)
{
	if(f[v]==v)
	{
		return v;

	}
	else
	{
		f[v]=getf(f[v]);
		return f[v]; 
	} 
}
int merge(int v,int u)
{
	int t1,t2;
	t1=getf(v);
	t2=getf(u);
	if(t1!=t2)
	{
		f[t2]=t1;
		return 1;
	}
	return 0;
}
int main()
{
	int i;
	while(scanf("%d %d",&n,&m)&&n!=0)
	{
		int sum=0,count=0;
		for(i=1;i<=m;i++)
	{
		scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
	}
	sort(e+1,e+m+1,cmp); 
	
	//Kruskal算法
	for(i=1;i<=n;i++)
	{
		f[i]=i;
	}
	for(i=1;i<=m;i++)
	{
		if(merge(e[i].u,e[i].v))//并查集判断是否有环 
		{
			count++;
			sum+=e[i].w;
		}
		if(count==n-1)
			break;
	}
	printf("%d\n",sum); 
	}
	
	return 0;
}

你可能感兴趣的:(算法,算法,数据结构,图论)