进阶版神犇可以看看本题解的姊妹篇 Kruskal算法的学习和使用 
下面的内容是prim算法

但是最小生成树是什么呢?

标准定义如下:在边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。

听起来非常的带劲,我们就一起来探讨这一求最小生成树的算法!

prim 的四大特征:

●最小生成树算法中prim算法是耗时最长的

●最小生成树算法中prim算法是适用于求稠密图的

●最小生成树算法中prime算法最简单易懂

●请不要多打一个e否则就是prime质数了(手动滑稽)

例子:

P3366 【模板】最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz

输入输出格式

输入格式:

第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)

接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi

输出格式:

输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz

输入输出样例

输入样例#1:


4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出样例#1:


说明

时空限制:1000ms,128M

数据规模:

对于20%的数据:N<=5,M<=20

对于40%的数据:N<=50,M<=2500

对于70%的数据:N<=500,M<=10000

对于100%的数据:N<=5000,M<=200000

样例解释:

所以最小生成树的总边权为2+2+3=7

【解析】

先来看一个例子,鲜明的阐释了prim算法的工作原理

原理:假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:

在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。

此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。

 Prim算法的核心:始终保持TE中的边集构成一棵生成树。

图例 说明 不可选 可选 已选(Vnew)
此为原始的加权连通图。每条边一侧的数字代表其权值。 - - -
顶点D被任意选为起始点。顶点A、B、E和F通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 C, G A, B, E, F D
下一个顶点为距离D或A最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F距D或A最近,因此将顶点F与相应边DF以高亮表示。 C, G B, E, F A, D
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。 C B, E, G A, D, F
在当前情况下,可以在C、E与G间进行选择。C距B为8,E距B为7,G距F为11。点E最近,因此将顶点E与相应边BE高亮表示。
C, E, G A, D, F, B
这里,可供选择的顶点只有C和G。C距E为5,G距E为9,故选取C,并与边EC一同高亮表示。 C, G A, D, F, B, E
顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG。 G A, D, F, B, E, C
现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 A, D, F, B, E, C, G

对于题目里的例子:我们来分析:


由于求的是最小生成树的边权之和,所以那边是最小生成树的根就无所谓了

这里假设1为最小生成树的根;


与集合{1}相连的边中只有权为2的边权最小,有2条边满足条件,

为了操作方便,选择靠后的1 3这条边加入集合(等价于前面的加入集合)把3加入集合{1}==>{1,3}


与集合{1,3}相连的边一共有4条,其中1 2之间的边权最小,所以把2加入集合{1,,3}==>{1,2,3}


与集合{1,2,3}相连的边一共有两条,都是3,

为了操作方便,选择靠后的1 4这条边加入集合(等价于前面的加入集合){1,2,3}==>{1,2,3,4}


发现所有的点都被加入集合,结束 权值和为2+2+3=7


【实现】

var n,m,i,j,u,v,d:longint;
g:array[..,..]of longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a)
else exit(b);
end;
procedure prim(v0:longint);
var u:array[..]of boolean;
min:array[..]of longint;
i,j,k,tot:longint;
begin
fillchar(min,sizeof(min),$7f);//min表示各点到集合{}的权值
fillchar(u,sizeof(u),true);//判断这个点是否加入集合
min[v0]:=;
d[v0]:=0;//自己到自己设为0
for i:=2 to n do begin//由于下面加入点为1则从2开始遍历(1也可以)
k:=;//k表示下一个被加入集合的点
for j:= to n do
if u[j] and (min[j]<min[k]) then k:=j;//迭代法找到下一个与集合{}间权值最小的点
u[k]:=false;//把这个点加入集合
for j:= to n do
if u[j] and (g[k,j]<min[j]) then min[j]:=g[k,j];
//由于新加入一个点也属于集合中所以各点到集合的距离可能会是这个点到k点的距离,所以判
end;
tot:=;
for i:= to n do tot:=tot+min[i];//求出集合到各点之间的距离记为tot
writeln(tot);//tot就是最小生成树的权值和
end;
begin
readln(n,m);
if m<n- then begin writeln('orz'); halt; end;//最小生成树的边一定等于点数-1,边数小于点数-1就不构成一个联通的图
fillchar(g,sizeof(g),$7f);//清零,用fillchar快不少
for i:= to m do begin
readln(u,v,d);
g[u,v]:=min(g[u,v],d);
g[v,u]:=g[u,v];//避免重复读入求最小边
end;
prim();//假定最小生成树的根为1
end.

【区别】

看完prim算法实现后,我们思考这样一个问题,单元最短路径的dijkstra的算法和prim有何相似之处呢?

不妨把dijkstra放在这里给大家显示一下区别吧!

例子:

P3371 【模板】单源最短路径

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

输入输出样例

输入样例#1:


输出样例#1:

   

说明

时空限制:1000ms,128M

数据规模:

对于20%的数据:N<=5,M<=15

对于40%的数据:N<=100,M<=10000

对于70%的数据:N<=1000,M<=100000

对于100%的数据:N<=10000,M<=500000

样例说明:

dijkstra算法详解:

单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。

一.最短路径的最优子结构性质

该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

假设P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P'(k,s),那么P'(i,j)=P(i,k)+P'(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。

二.Dijkstra算法

由上述性质可知,如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点。那么(Vi...Vk)也必定是从i到k的最短路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根据这种思路,

假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。

1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;

2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})

3.知道U=V,停止。

三、流程图

【dijkstra程序实现】

const longlong=;
maxn=;
var i,j,n,m,s,x,y,z:longint;
d:array[..maxn]of longint;
g:array[..maxn,..maxn]of longint;
function min(a,b:longint):longint;
begin
if a>b then exit(b) else exit(a);
end;
procedure dijkstra(v0:longint);
var i,j,k,minn:longint;
u:array[..maxn]of boolean;
begin
fillchar(u,sizeof(u),false);
for i:= to n do d[i]:=g[v0,i];d[v0]:=;//初始值设为所有点到这个点的最短距离
u[v0]:=true;//加入v0到集合{}
for i:= to n do begin//由于不知道是那个点所以不能简单从2开始,若从2开始就是当v0=1时才正确
minn:=longlong; k:=;//minn表示d[]数组中最小的数,k表示这个最小的数是属于哪个点的
for j:= to n do
if (not u[j])and(d[j]<minn) then begin
minn:=d[j]; k:=j;//求minn和k
end;
if k= then break;// 如果找不到点说明所有点都找完了说明d[]存储的就是各点到v0的单源最短路,跳出循环
u[k]:=true;//加入最短距离k到集合{}表明此时k到v0的距离已经是最短了
for j:= to n do
if (not u[j])and(g[k,j]+d[k]<d[j])then//由于集合中新加入一个点,所以相对应的所有点可能通过到这个新加入的点在到v0是最短路
d[j]:=g[k,j]+d[k];//更改最短距离
end;
end;
begin
readln(n,m,s);
for i:= to n do
for j:= to n do
if i<>j then g[i,j]:=longlong
else g[i,j]:=;
for i:= to m do begin
readln(x,y,z);
g[x,y]:=min(g[x,y],z);
end;
dijkstra(s);
for i:= to n do write(d[i],' ');//d数组中存的是点1-n到点s的最短单源路径!
writeln;
end.

【异同点】

区别:

1.用途:

Prim是计算最小生成树的算法,比如为N个村庄修路,怎么修花销最少。

Dijkstra是计算最短路径的算法,比如从a村庄走到其他任意村庄的距离。

2.概念

Prim出现点到集合的距离(这里用贪心),即这个点到集合中各元素的距离最小才加入最小的边

dijkstra中出现的点到集合的距离是通过每个待定的中转点来实现两点之间距离最短的

相似:

1.有关负权回路,prim和dijkstra在该问题上都是不能运行的

2.朴素的时间复杂度都是O(N2)的

3.求解方法,都是假设全部顶点的集合是V,已经被挑选出来的点的集合是U,那么二者都是从集合V-U中不断的挑选权值最低的点加入U的

【prim的优化】

这里引用百度百科的一段话:

通过邻接矩阵图表示的简易实现中,找到所有最小权边共需O(V)的运行时间。使用简单的二叉堆与邻接表来表示的话,普里姆算法的运行时间则可缩减为O(ElogV),其中E为连通图的边数,V为顶点数。如果使用较为复杂的斐波那契堆,则可将运行时间进一步缩短为O(E+VlogV),这在连通图足够密集时(当E满足Ω(VlogV)条件时),可较显著地提高运行速度。

最小边、权的数据结构 时间复杂度(总计)
邻接矩阵、搜索 O(V^2)
二叉堆、邻接表 O((V + E) log(V)) = O(E log(V))
斐波那契堆、邻接表 O(E + V log(V))
 
接下来给大家推荐几个好的关于prim 优化的文章,供大家参考:
 
●二叉堆维护+邻接表:链接
 
●斐波那契堆维护+邻接表:链接

【树论 1】 prim算法的学习和使用的更多相关文章

  1. 【树论 2】Kruskal 的学习和使用

    Tips:本题解是[随便搞搞 1]Prim算法的学习和使用 的姊妹篇,希望先阅读Prim算法. 预习及预备知识: 克鲁斯卡尔(Kruskal)算法是实现图的最小生成树最常用的算法. 大家知道,存储图的 ...

  2. 2017年中国大学生程序设计竞赛-中南地区赛暨第八届湘潭市大学生计算机程序设计大赛题解&源码(A.高斯消元,D,模拟,E,前缀和,F,LCS,H,Prim算法,I,胡搞,J,树状数组)

    A------------------------------------------------------------------------------------ 题目链接:http://20 ...

  3. 算法导论学习-prim算法

    一. 关于最小生成树 对于无向连通图G=(V,E),其中V表示图的顶点,E表示图的边,对于每条边都有一个权值,可以理解为边a->b的权值C为从a走到b要走的路程为C.现在我们希望找到一个无回路的 ...

  4. 算法学习记录-图——最小生成树之prim算法

    一个连通图的生成树是一个极小的连通子图,它包含图中全部的顶点(n个顶点),但只有n-1条边. 最小生成树:构造连通网的最小代价(最小权值)生成树. prim算法在严蔚敏树上有解释,但是都是数学语言,很 ...

  5. 最小生成树问题---Prim算法学习

    一个具有n个节点的连通图的生成树是原图的最小连通子集,它包含了n个节点和n-1条边.若砍去任一条边,则生成树变为非连通图:若增加一条边,则在图中形成一条回路.本文所写的是一个带权的无向连通图中寻求各边 ...

  6. (学习1)最小生成树-Prim算法与Kruskal算法

    最小生成树: 求一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. 1:Prim算法(适合稠密图) 伪代码: Prim(G){ //G ...

  7. 数据结构(C实现)------- 最小生成树之Prim算法

    [本文是自己学习所做笔记.欢迎转载.但请注明出处:http://blog.csdn.net/jesson20121020] 算法描写叙述 假设连通图是一个网,则称该网中全部生成树中权值总和最小的生成树 ...

  8. 【数据结构】 最小生成树(三)——prim算法

    上一期介绍到了kruskal算法,这个算法诞生于1956年,重难点就是如何判断是否形成回路,此处要用到并查集,不会用当然会觉得难,今天介绍的prim算法在kruskal算法之后一年(即1957年)诞生 ...

  9. 最小生成树的本质是什么?Prim算法道破天机

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法和数据结构专题20篇文章,我们继续最小生成树算法,来把它说完. 在上一篇文章当中,我们主要学习了最小生成树的Kruskal算法.今 ...

随机推荐

  1. Windows系统环境变量之path环境变量(Java, Python环境变量配置)

    系统: Windows10 path系统环境变量的作用: Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外 ...

  2. Python实现学生系统

    # 4. 修改之前的学生信息管理程序,实现添加菜单和选择菜单操作功能: # 菜单: # +-----------------------------+ # | 1) 添加学生信息 | # | 2) 查 ...

  3. 如何获取c:forEach里面点击时候的值

    1.c:forEach遍历输出 <c:forEach items="${data}" var="item" > <a onclick=&quo ...

  4. Linux常规命令总结

    Linux常规命令总结,仅供参考: 系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显 ...

  5. JavaScript学习要点

    Javascript相关内容 1.序列化--json - stringify() 将对象转换为字符串 - parse() 将字符串转换为对象 list=[11,22,33,44,55]; 结果:(5) ...

  6. VGG——Very deep convolutional networks for large-scale image recognition

    1. 摘要 在使用非常小(3×3)的卷积核情况下,作者对逐渐增加网络的深度进行了全面的评估,通过设置网络层数达 16-19 层,最终效果取得了显著提升. 2. 介绍 近来,卷积神经网络在大规模图像识别 ...

  7. Python基础_可迭代的/迭代器/生成器

    介绍 可迭代的:内部实现了__iter__方法 迭代器:内部实现了__iter__,__next__方法 生成器:yield,yield from 使用 __iter__() __next__() _ ...

  8. ubuntu16更新源

    http://blog.csdn.net/fengyuzhiren/article/details/54844870

  9. 作业1MathExam

    自己取一个大气又可爱的标题 一.预估与实际 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 720 1000 ...

  10. Sprint2

    进展:主要进行了在安卓手机端进行APP开发的资料及有关学习的视频的查找等.了解也学习了这些资料还有技术.第一个任务完成了一半. 燃尽图: