hdu 1233 还是畅通工程(最小生成树的Prim和Kruskal两种算法的c++实现)(prim算法详解)...
赤裸裸滴最小生成树(MST),刚学的玩意,用两种方法熟练一下。(都是greedy)
Kruskal方法:先对边按照代价非递减排序,再不断添加边且不产生环路,当边数=点数-1结束。判断加入(v,w)是否会产生环路,可以用并查集,如果检查v和w在同一集合中,说明这两个点已经连通,加入边(v, w)就会产生环路。Kruskal算法总时间复杂度O(eloge).
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 const int MAXN = 5050; 5 int n, father[MAXN], m; //m边数 6 struct Edge 7 { 8 int x, y, length; 9 bool operator<(const Edge a) const 10 { 11 return length < a.length; 12 } 13 }; 14 Edge e[MAXN]; 15 void Init(int *a) 16 { 17 for (int i = 1; i <= n; ++i) 18 father[i] = i; 19 } 20 int Find(int x) 21 { 22 if (x != father[x]) 23 father[x] = Find(father[x]); 24 return father[x]; 25 } 26 void Union(int x, int y) 27 { 28 int fx = Find(x), fy = Find(y); 29 if (fx != fy) 30 father[fx] = fy; 31 } 32 int Kruskal() 33 { 34 std::sort(e, e + m); 35 Init(father); 36 int sum = 0; 37 for (int i = 0; i < m; ++i) 38 if (Find(e[i].x) != Find(e[i].y)) 39 { 40 Union(e[i].x, e[i].y); 41 sum += e[i].length; 42 } 43 return sum; 44 } 45 int main() 46 { 47 while (scanf("%d", &n) != EOF && n) 48 { 49 m = n * (n - 1) / 2; 50 for (int i = 0; i < m; ++i) 51 scanf("%d %d %d", &e[i].x, &e[i].y, &e[i].length); 52 printf("%d\n", Kruskal()); 53 } 54 return 0; 55 }
Prim算法:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 const int MAXN = 101; 5 const int INF = INT_MAX; 6 int n, m, visited[MAXN], dist[MAXN]; //dist[i]记录i到与i邻接且未被访问的点的最小权值 7 int map[MAXN][MAXN]; 8 int Prim(int n) 9 { 10 memset(visited, 0, sizeof(visited)); 11 visited[1] = 1; 12 dist[1] = INF; 13 for (int i = 2; i <= n; ++i) 14 dist[i] = map[1][i]; 15 int min, pos, sum = 0; 16 for (int i = 2; i <= n; ++i) //加入剩下的n-1个点 17 { 18 min = INF; 19 for (int j = 1; j <= n; ++j) 20 if (!visited[j] && dist[j] < min) 21 { 22 min = dist[j]; 23 pos = j; 24 } 25 sum += min; //或者sum += dist[pos]; 26 visited[pos] = 1; 27 for (int j = 1; j <= n; ++j) //刷新最小权值 28 if (!visited[j] && map[pos][j] < dist[j]) 29 dist[j] = map[pos][j]; 30 } 31 return sum; 32 } 33 int main() 34 { 35 while (scanf("%d", &n) != EOF && n) 36 { 37 m = n * (n - 1) / 2; 38 int x, y, len; 39 for (int i = 0; i < m; ++i) //建图 40 { 41 scanf("%d %d %d", &x, &y, &len); 42 map[x][y] = map[y][x] = len; 43 } 44 for (int i = 0; i <= n; ++i) 45 map[i][i] = INF; 46 printf("%d\n", Prim(n)); 47 } 48 return 0; 49 }
还不是很熟悉,贴一个资料先学一下。
假设G=(V,E)为一网图,其中V 为网图中所有顶点的集合,E 为网图中所有带权边的集合。设置两个新的集合U 和T,其中集合U 用于存放G 的最小生成树中的顶点,集合T 存放G 的最小生成树中的边。令集合U 的初值为U={u1}(假设构造最小生成树时,从顶点u1 出发),集合T 的初值为T={}。Prim 算法的思想是,从所有u∈U,v∈V-U 的边中,选取具有最小权值的边(u,v),将顶点v 加入集合U 中,将边(u,v)加入集合T 中,如此不断重复,直到U=V 时,最小生成树构造完毕,这时集合T 中包含了最小生成树的所有边。
Prim 算法可用下述过程描述,其中用wuv 表示顶点u 与顶点v 边上的权值。
(1)U={u1},T={};
(2)while (U≠V)do
(u,v)=min{wuv;u∈U,v∈V-U }
T=T+{(u,v)}
U=U+{v}
(3)结束。
图8.23 (a)所示的一个网图,按照Prim 方法,从顶点1 出发,该网的最小生成树的产生过程如图8.23 (b)、(c)、(d)、(e)、(f)和(g)所示。
为实现Prim 算法,需设置两个辅助一维数组lowcost 和closevert,其中lowcost 用来保存集合V-U 中各顶点与集合U 中各顶点构成的边中具有最小权值的边的权值;数组closevertex 用来保存依附于该边的在集合U 中的顶点。假设初始状态时,U={u1}(u1 为出发的顶点),这时有lowcost[0]=0,它表示顶点u1 已加入集合U 中,数组lowcost 的其它各分量的值是顶点u1 到其余各顶点所构成的直接边的权值。然后不断选取权值最小的边(ui,uk)(ui∈U,uk∈V-U),每选取一条边,就将lowcost(k)置为0,表示顶点uk 已加入集合U 中。由于顶点uk 从集合V-U 进入集合U 后,这两个集合的内容发生了变化,就需依据具体情况更新数组lowcost 和closevertex 中部分分量的内容。最后closevertex 中即为所建立的最小生成树。
当无向网采用二维数组存储的邻接矩阵存储时,Prim 算法的C 语言实现为:
先从某一点开始,把这一个开始的点放于声明的一个数组或者集合里,表明这一点已经被访问过。然后再从余下的n-1个点里去找那个权值最小的点并记录该点的位置然后再加上这一点的权值,同时将该点放于集合里表明该点已初访问。再更新权值
void Prim(int gm[][MAXNODE],int n,int closevertex[]) {/*用Prim 方法建立有n 个顶点的邻接矩阵存储结构的网图gm 的最小生成树*/ /*从序号为0 的顶点出发;建立的最小生成树存于数组closevertex 中*/int lowcost[100],mincost;int i,j,k;for (i=1;i<n;i++) /*初始化*/{lowcost[i]=gm[0][i];closevertex[i]=0;}lowcost[0]=0; /*从序号为0 的顶点出发生成最小生成树*/closevertex[0]=0;for (i=1;i<n;i++) /*寻找当前最小权值的边的顶点*/{mincost=MAXCOST; /*MAXCOST 为一个极大的常量值*/j=1;k=1;while (j<n){ if (lowcost[j]<mincost && lowcost[j]!=0){ mincost=lowcost[j];k=j;}j++;}printf(“顶点的序号=%d 边的权值=%d\n”,k,mincost);lowcost[k]=0;for (j=1;j<n;j++) /*修改其它顶点的边的权值和最小生成树顶点序号*/if (gm[k][j]<lowcost[j]){ lowcost[j]=gm[k][j];closevertex[j]=k;}} }
算法8.14
图8.24 给出了在用上述算法构造网图8.23 (a)的最小生成树的过程中,数组closevertex、lowcost 及集合U,V-U 的变化情况,读者可进一步加深对Prim 算法的了解。
在Prim 算法中,第一个for 循环的执行次数为n-1,第二个for 循环中又包括了一个while 循环和一个for 循环,执行次数为2(n-1)2,所以Prim 算法的时间复杂度为O(n2)。
关于prim算法
先把有的点放于一个集合(或者数组)里,这个集合里存放的是所有走过的点。初始值为0或者false表示还没有点
声明一个一维数组用于记录各点的权值[可理解为起始点到目标点的距离],
声明一个二维数组用于记录某点到某一点的权值,如果这两点不可达到,则设置为无穷大
具体执行过程:
先从某一点开始,把这一个开始的点放于声明的一个数组或者集合里,表明这一点已经被访问过。然后再从余下的n-1个点里去找那个权值最小的点并记录该点的位置然后再加上这一点的权值,同时将该点放于集合里表明该点已初访问。再更新权值
再看下图,从下图分析:
1、
先选取一个点作起始点,然后选择它邻近的权值最小的点(如果有多个与其相连的相同最小权值的点,随便选取一个)。如1作为起点。
isvisited[1]=1; //表明把1加进来说明是已经访问过
pos=1; //记录该位置
//用dist[]数组不断刷新最小权值,dist[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最小距离。
dist[1]=0; //起始点i到邻近点的最小距离为0
dist[2]=map[pos][2]=4;
dist[3]=map[pos][3]=2;
dist[4]==map[pos][4]=3;
dist[5]=map[pos][5]=MaxInt; //无法直达
dist[6]=map[pos][6]=MaxInt;
2、
再在伸延的点找与它邻近的两者权值最小的点。
//dist[]以3作当前位置进行更新
isvisited[3]=1;
pos=3;
dist[1]=0; //已标记,不更新
dist[2]=map[pos][2]=4; //比5小,不更新
dist[3]=2; //已标记,不更新
dist[4]=map[pos][4]=3; //比1大,更新
dist[5]=map[pos][5]=MaxInt;
dist[6]=map[pos][6]=MaxInt;
3、最后的结果:
当所有点都连同后,结果最生成树如上图所示。
所有权值相加就是最小生成树,其值为2+1+2+4+3=12。
prim算法的实现:
- //prim算法
- int prim(int n){
- int i,j,min,pos;
- int sum=0;
- memset(isvisited,false,sizeof(isvisited));
- //初始化
- for(i=1;i<=n;i++){
- dist[i]=map[1][i];
- }
- //从1开始
- isvisited[1]=true;
- dist[1]=MAX;
- //找到权值最小点并记录下位置
- for(i=1;i<n;i++){
- min=MAX;
- //pos=-1;
- for(j=1;j<=n;j++){
- if(!isvisited[j] && dist[j]<min){
- min=dist[j];
- pos=j;
- }
- }
- sum+=dist[pos];//加上权值
- isvisited[pos]=true;
- //更新权值
- for(j=1;j<=n;j++){
- if(!isvisited[j] && dist[j]>map[pos][j]){
- dist[j]=map[pos][j];
- }
- }
- }
- return sum;
- }
转载于:https://www.cnblogs.com/PegasusWang/archive/2013/04/29/3050783.html
hdu 1233 还是畅通工程(最小生成树的Prim和Kruskal两种算法的c++实现)(prim算法详解)...相关推荐
- hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)
还是畅通工程 Time Limit: 4000/2 ...
- 用Prim和Kruskal两种算法,求解最小生成树
本文通过具体的算法模板题,给出Prim和Kruskal两种求解最小生成树求解过程和代码~ 由浅入深,通俗易懂 题目选自洛谷P3366 题目描述 如题,给出一个无向图,求出最小生成树,如果该图不连通,则 ...
- HDU 1233 还是畅通工程(最小生成树 Prim+Kruskal)
原题地址 http://acm.hdu.edu.cn/showproblem.php?pid=1233 题意:(最小生成树裸题)有N个村庄,给出村庄两两之间的距离,要求铺设公路,使得任何两个村庄间都可 ...
- HDU 1233 还是畅通工程(最小生成树)
传送门 还是畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...
- hdu 1879 继续畅通工程 最小生成树
继续畅通工程 Time Limit: 2000/ ...
- HDU 1879 继续畅通工程 最小生成树
继续畅通工程 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Description ...
- hdu 1879 继续畅通工程 (最小生成树)
继续畅通工程 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...
- HDU 1233 - 还是畅通工程
Problem Description 某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离.省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直 ...
- hdu 1233 还是畅通工程 Kruskal 最小生成树 并查集
题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=1233 模板题,kruskal求最小生成树. 并查集是个好东西啊 就是注意一点 输入边的信息时,角标 ...
最新文章
- 可持久化链表(链式前向星)
- 编译 ioftpd v7.7.3
- zoj 1962 How Many Fibs?(字符串化为数字处理)
- Hive动态分区 参数配置及语法
- 【 HRBUST - 1055】Single(模拟,dp,打表)(总结)
- 基于visual Studio2013解决面试题之0307最后谁剩下
- 打印更无缝:微软改善Win11中通用打印体验
- 背包九讲之三(多重背包)
- Asp.net上传文件至目录
- 4级网络工程师第5套知识点
- 金融量化之华泰多因子估值类显著性和IC值计算
- Paper reading:A simple baseline for one-shot multi-object tracking(二)
- git tracked branch
- hdu 畅通工程再续
- spring的依赖注入是什么意思
- Flutter 游戏引擎 flame
- SVG进阶-SMIL动画
- 数据可视化路上——中国地图可视化
- nginx交叉编译流程
- python实现烤羊肉串(类)