newwy
奋斗在IT路上的小蜗牛。一步一步往上爬,爬到小牛,在到大牛,然后是神牛,然后是犇,然后就可以离开IT行业,回归大自然了。 远离IT,珍爱生命!!! 记录学习的点滴。

  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;
}


posted on 2010-10-31 15:17  newwy  阅读(570)  评论(0编辑  收藏  举报