Prim算法用于求无向图的最小生成树
设图G =(V,E),是一个具有n个顶点的带权连通图,T=(U,TE)是G的最小生成树,期中U是T的顶点集,TE是T的边集,则从V0开始构造最小生成树T的步骤如下:
(1) 初始化 = {V0}将V0到其他顶点的所有边作为候选边。
(2) 重复以下步骤n-1次,使得其他n-1个顶点被加入到U中。
- 从候选边中挑选权值最小的边输出,设该边在V-U中的顶点是V,将V加入U中,删除和V关联的边。
- 考察当前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时,输入结束,该用例不被处理。
当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; }