现在的位置: 首页 > 综合 > 正文

最小生成树——prim算法

2013年10月09日 ⁄ 综合 ⁄ 共 4026字 ⁄ 字号 评论关闭

所谓生成树,就是n个点之间连成n-1条边的图形。而最小生成树,就是权值(两点间直线的值)之和的最小值。


http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/flashhtml/prim.htm(prim构成最小生成树的演示过程)。


其实原理很简单就是两个集合:一个是图向量的集合,一个是要求的最小生成树的集合,然后就是选择它邻近的最小权值对应的顶点,把顶点和边放入到要求的集合,直到所有元素都放入为止。

Prim算法与Dijkstra算法的区别

在图论中,Prim算法是计算最小生成树的算法,而Dijkstra算法是计算最短路径的算法。二者看起来比较类似,因为假设全部顶点的集合是V,已经被挑选出来的点的集合是U,那么二者都是从集合V-U中不断的挑选权值最低的点加入U,那么二者是否等价呢?也就是说是否Dijkstra也可以计算出最小生成树而Prim也可以计算出从第一个顶点v0到其他点的最短路径呢?答案是否定的,否则就不必有两个算法了。

二者的不同之处在于“权值最低”的定义不同,Prim的“权值最低”是相对于U中的任意一点而言的,也就是把U中的点看成一个整体,每次寻找V-U中跟U的距离最小(也就是跟U中任意一点的距离最小)的一点加入U;而Dijkstra的“权值最低”是相对于v0而言的,也就是每次寻找V-U中跟v0的距离最小的一点加入U。

一个可以说明二者不等价的例子是有四个顶点(v0, v1, v2, v3)和四条边且边值定义为(v0, v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的图,用Prim算法得到的最小生成树中v0跟v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。

相关的应用问题:http://poj.org/problem?id=1258

程序1:

#include<iostream>

using namespace std;

int map[101][101];

int visited[101];

int main() {

int n;

while(cin>>n) {

memset(visited,0,sizeof(visited));

int i,j,k;

memset(map,0,sizeof(map));

for(i=0;i<n;i++)

for(j=0;j<n;j++)

scanf("%d",&map[i][j]);

visited[0]=1;

int temp=0;

int sum=0;

for(i=1;i<n;i++) {

int min=1000000;

for(j=0;j<n;j++)

if(visited[j])

{

for(k=0;k<n;k++){

if(!visited[k]&&map[j][k]<min)

min=map[j][k],temp=k;

}

}

sum+=min;

visited[temp]=1;

}

cout<<sum<<endl;

}

return 0;

程序2:

#include <stdio.h>

#include <string.h>

#define MaxInt 0x3f3f3f3f

#define N 110
//创建map二维数组储存图表,low数组记录每2个点间最小权值,visited数组标记某点是否已访问
int map[N][N],low[N],visited[N];
int n;

int prim(){

 int i,j,pos,min,result=0;
 memset(visited,0,sizeof(visited));
//从某点开始,分别标记和记录该点
 visited[1]=1;
 pos=1;
//第一次给low数组赋值
 for(i=1;i<=n;i++)
 {
  if(i!=pos)
   low[i]=map[pos][i];
 }
//再运行n-1次
 for(i=1;i<n;i++)
 {
//找出最小权值并记录位置
  min=MaxInt;
  for(j=1;j<=n;j++)
  {
   if(visited[j]==0&&min>low[j])
   {
    min=low[j];
    pos=j;
   }
  }
//最小权值累加
  result+=min;
//标记该点
  visited[pos]=1;
//更新权值
  for(j=1;j<=n;j++)
   if(visited[j]==0&&low[j]>map[pos][j])
    low[j]=map[pos][j];
 }
 return result;
}

int main()
{
 int i,v,j,ans;
 while(scanf("%d",&n)!=EOF)
 {
//所有权值初始化为最大
  memset(map,MaxInt,sizeof(map));
  for(i=1;i<=n;i++)
   for(j=1;j<=n;j++)
   {
    scanf("%d",&v);
    map[i][j]=map[i][j]=v;
   }
  ans=prim();
  printf("%d\n",ans);
 }
 return 0;
}

在图论中,Prim算法是计算最小生成树的算法,而Dijkstra算法是计算最短路径的算法。二者看起来比较类似,因为假设全部顶点的集合是V,已经被挑选出来的点的集合是U,那么二者都是从集合V-U中不断的挑选权值最低的点加入U,那么二者是否等价呢?也就是说是否Dijkstra也可以计算出最小生成树而Prim也可以计算出从第一个顶点v0到其他点的最短路径呢?答案是否定的,否则就不必有两个算法了。

二者的不同之处在于“权值最低”的定义不同,Prim的“权值最低”是相对于U中的任意一点而言的,也就是把U中的点看成一个整体,每次寻找V-U中跟U的距离最小(也就是跟U中任意一点的距离最小)的一点加入U;而Dijkstra的“权值最低”是相对于v0而言的,也就是每次寻找V-U中跟v0的距离最小的一点加入U。

一个可以说明二者不等价的例子是有四个顶点(v0, v1, v2, v3)和四条边且边值定义为(v0, v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的图,用Prim算法得到的最小生成树中v0跟v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。

相关的应用问题:http://poj.org/problem?id=1258

程序1:

#include<iostream>

using namespace std;

int map[101][101];

int visited[101];

int main() {

int n;

while(cin>>n) {

memset(visited,0,sizeof(visited));

int i,j,k;

memset(map,0,sizeof(map));

for(i=0;i<n;i++)

for(j=0;j<n;j++)

scanf("%d",&map[i][j]);

visited[0]=1;

int temp=0;

int sum=0;

for(i=1;i<n;i++) {

int min=1000000;

for(j=0;j<n;j++)

if(visited[j])

{

for(k=0;k<n;k++){

if(!visited[k]&&map[j][k]<min)

min=map[j][k],temp=k;

}

}

sum+=min;

visited[temp]=1;

}

cout<<sum<<endl;

}

return 0;

程序2:

#include <stdio.h>

#include <string.h>

#define MaxInt 0x3f3f3f3f

#define N 110
//创建map二维数组储存图表,low数组记录每2个点间最小权值,visited数组标记某点是否已访问
int map[N][N],low[N],visited[N];
int n;

int prim(){

 int i,j,pos,min,result=0;
 memset(visited,0,sizeof(visited));
//从某点开始,分别标记和记录该点
 visited[1]=1;
 pos=1;
//第一次给low数组赋值
 for(i=1;i<=n;i++)
 {
  if(i!=pos)
   low[i]=map[pos][i];
 }
//再运行n-1次
 for(i=1;i<n;i++)
 {
//找出最小权值并记录位置
  min=MaxInt;
  for(j=1;j<=n;j++)
  {
   if(visited[j]==0&&min>low[j])
   {
    min=low[j];
    pos=j;
   }
  }
//最小权值累加
  result+=min;
//标记该点
  visited[pos]=1;
//更新权值
  for(j=1;j<=n;j++)
   if(visited[j]==0&&low[j]>map[pos][j])
    low[j]=map[pos][j];
 }
 return result;
}

int main()
{
 int i,v,j,ans;
 while(scanf("%d",&n)!=EOF)
 {
//所有权值初始化为最大
  memset(map,MaxInt,sizeof(map));
  for(i=1;i<=n;i++)
   for(j=1;j<=n;j++)
   {
    scanf("%d",&v);
    map[i][j]=map[i][j]=v;
   }
  ans=prim();
  printf("%d\n",ans);
 }
 return 0;
}

抱歉!评论已关闭.