Prim算法,最小生成树 HDU 1233

  Prim算法用于求无向图的最小生成树

设图G =(V,E),是一个具有n个顶点的带权连通图,T=(U,TE)是G的最小生成树,期中U是T的顶点集,TE是T的边集,则从V0开始构造最小生成树T的步骤如下:

(1)      初始化 = {V0}将V0到其他顶点的所有边作为候选边。

(2)      重复以下步骤n-1次,使得其他n-1个顶点被加入到U中。

  1. 从候选边中挑选权值最小的边输出,设该边在V-U中的顶点是V,将V加入U中,删除和V关联的边。
  2. 考察当前V-U中的所有顶点Vi,修正候选边。若(V,Vi)的权值小于原来和Vi关联的候选边,则用(V,Vi)取代后者作为候选边。
void Prim(int n,int Graph[][MAXN],int * pre)

{

	int vset[MAXN],lowcost[MAXN];//lowcost[MAXN]表示存放顶点到为完成的生成树的最小权值

								//v[MAXN]表示该结点是否已经在生成树里 

	int i,j,k;				

	for(i = 0 ; i < n; i++)		//赋初始值,将每个结点的连接到生成树的权值赋值为最大,所有结点均未在生成树中

								//所有结点的前驱都是-1,表示没有任何结点与之相连 

	{lowcost[i] = INF;vset[i] = 0;pre[i] = -1;}

	

	for(lowcost[j = 0] = 0;j < n; j++)//分别将连通图中n个结点,加入到最小生成树中

	{

		for(k = -1,i = 0; i < n; i++)//第一次循环将k 赋值 -1,这样找到第一个没有加入到生成树中的结点,之后k值改变

		{							//继续循环,寻找不在生成树中的结点到生成树的权值最小的那个结点,将其链接到生成树上

			if(vset[i] == 0 &&(k == -1 || lowcost[i] < lowcost[k]))

				k = i;

		}

		printf("edge(%d,%d)的权值为%d,被加入生成树中\n",pre[k]+1,k+1,lowcost[k]);

		vset[k] = 1;				//将选中的结点做好标记 

		

		for(i = 0 ; i < n; i++)		//修正候选边,每次记录入选的结点k之后。分别记录其他结点到生成树的最小权值

		{

			if(vset[i] == 0 && Graph[k][i] < lowcost[i])

			{



				lowcost[i] = Graph[k][i];	

				pre[i] = k;				//pre记录i结点到生成树最小权值是连接在k结点上

			}

		} 

	} 

}

 

HDU 1233 还是畅通工程

Problem Description
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
 

 

Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
 

 

Output
对每个测试用例,在1行里输出最小的公路总长度。
 

 

Sample Input
  
    
31 2 11 3 22 3 441 2 11 3 41 4 12 3 32 4 23 4 50
 

 

Sample Output
  
    
35
#include <iostream>

using namespace std;



//无向图最小生成树,prim算法,邻接阵形式,复杂度O(n^2)

//返回最小生成树的长度,传入图的大小n和邻接阵mat,不相邻点边权inf

//可更改边权的类型,pre[]返回树的构造,用父结点表示,根节点(第一个)pre值为-1

//必须保证图的连通的!

#define MAXN 200

#define inf 1000000000

typedef int elem_t;



elem_t prim(int n,elem_t mat[][MAXN],int* pre)

{

	elem_t min[MAXN],ret=0;			//min[MAXN]表示存放顶点到为完成的生成树的最小权值,ret作为本题的结果,

									//ret为本题最小生成树所有边的权值总和 

	int v[MAXN],i,j,k;				//v[MAXN]表示该结点是否已经在生成树里,如果在则为1,否则为0 



	for (i=0;i<n;i++)				//赋初始值,将每个结点的连接到生成树的权值赋值为最大,所有结点均未在生成树中 

		min[i]=inf,v[i]=0,pre[i]=-1;

		

	for (min[j=0]=0;j<n;j++)		//分别将连通图中n个结点,加入到最小生成树中 

	{ 

		for (k=-1,i=0;i<n;i++)		//第一次循环将k 赋值 -1,这样找到第一个没有加入到生成树中的结点,之后k值改变,

									//继续循环,寻找不在生成树中的结点到生成树的权值最小的那个结点,将其链接到生成树上 

			if (!v[i]&&(k==-1||min[i]<min[k]))

					k=i;  

					

		for (v[k]=1,ret+=min[k],i=0;i<n;i++)//修正候选边,每次记录入选的结点k之后。分别记录其他结点到生成树的最小权值 

			if (!v[i]&&mat[k][i]<min[i])

				min[i]=mat[pre[i]=k][i];	//pre记录i结点到生成树最小权值是连接在k结点上 

	}

	return ret;

}

int mat[MAXN][MAXN], pre[MAXN];

int main() 

{

	int n, i, j, u, v, c;

	while (scanf("%d", &n) != EOF) 

	{

		if (n == 0) break;

		for (i = 0; i < n * (n - 1) / 2; i++) 

		{

			scanf("%d%d%d", &u, &v, &c);

			

			u--, v--;

			mat[u][v] = mat[v][u] = c;

		}

		printf("%d\n", prim(n, mat, pre));

	}

	return 0;

}





你可能感兴趣的:(最小生成树)