您的位置:首页 > 其它

prim算法 zoj1372 博客园第一篇文章

2012-04-29 12:51 127 查看
今天是我在自己的博客里发表的第一篇文章, 希望以后能把自己的博客越办越好 ,也希望和大家一起讨论,研究,成长进步 。

好了废话不多说了!~开始我的题目。

zoj1372 友情链接 :http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=372

题目大意:给一个图,告诉其中的一些边,及其权值,让你找连通所有节点的最短路径和。

其实就是最小生成树问题。 我采用的是prim算法解决的。

首先讲一下prim算法: prim算法利用的是MST性质。

MST性质

N=(V,E,C)是一个连通网,U是V的一个非空子集,若u,v满足weight(u,v)=min{weight(u0,v0)│u0∈U,v0∈V-U}则必存在N的一棵最小支撑树,该支撑树中包括边(u,v)

有了MST性质之后问题就转化为了:初始条件为u0=起始节点,v0=所有节点-起始节点,

目标状态为u0=全部节点,v0=空集。

问题在于如何去将v0中的节点加入到u0中。。

嘿嘿....想必你已经知道了。 利用MST性质,找到连通u0和v0最短的一条边。

到这里应该就有思路了:(sum为所求的最短路径和)

1.首先初始化图,中的节点和边(没有边连接的点之间距离令为很大的一个数,表示这条边不能选)

2.把起始节点加入到一个集合u0(这里我们可以用一个数组来标记每个节,如hash[i]=1,表示把节点i加入到u0中,如果不在的话hash[i]==0)

3.找到距离节点u0最短的点u,将其并入u0中(hash[u]=1)。(注意到这是集合u0发生了改变,这是有些节点到u0的最短路径已经不是它到当初那个起始节点的距离了,可能是它到新进入的这个节点的距离,所以我们下面要对距离进行更新)

4.更新v0每个节点到u0的最短距离:(dis[i]=min{dis[i],map[u][i]}。

5.重复3和4的过程,知道结束。

这里问题有来了,什么时候才算结束呢?

你很容易想到:当所以的节点都满足hash[i]==1问题就解决,但这样毕竟会影响到效率,因为每进行一次循环,都要进行一次循环的判断,很费事。

仔细想一下会发现,如果有N个节点,那么并入节点的操作,应该是N-1次(每次并入一个点嘛)。所以只要循环N-1次就行了。

现在代码就呼之欲出了。

zoj 1372 AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#define MAX 9999999
using namespace std;

int map[55][55];       //用来表示图中的边 没有边的点之间置为0
int hash[55];          // 判断节点是否在u0中
int dis[55];           // 用来表示节点到集合u0的最短距离,无法到达置为很大的数
int N,M;

int prim();

int main(void)
{
int a,b,c;
while(scanf("%d",&N)!=EOF&&N)
{
getchar();
scanf("%d",&M);
getchar();
memset(map,0,sizeof(map));
memset(hash,0,sizeof(hash));
memset(dis,0,sizeof(dis));
for(int i=0;i<M;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]==0||map[a][b]>c)
{
map[a][b]=c;
map[b][a]=c;
}
}
printf("%d\n",prim());
}
// system("pause");
return 0;
}

int prim()
{
int sum=0;
for(int i=2;i<=N;i++)
{
dis[i]=MAX;
if(map[1][i])
{
dis[i]=map[1][i];
}
}
hash[1]=1;
dis[1]=0;
for(int k=2;k<=N;k++)                    //进行N-1次循环
{
int min=MAX,u=1;
for(int i=2;i<=N;i++)            //找距离u0最近的点
{
if(min>dis[i]&&!hash[i])
{
min=dis[i];
u=i;
}
}
hash[u]=1;
sum+=min;
for(int i=2;i<=N;i++)             //更新节点到u0的距离
{
if(map[u][i]&&dis[i]>map[u][i]&&!hash[i])  //当i与u之间有边 dis[i]=min{dis[i],map[u][i]}
{
dis[i]=map[u][i];
}
}
}
return sum;
}


最后我还想将prim与dijastra算法最下对比:

它两的代码很相似

prim得到的是最小生成树,最短连通路径,

而dijastra得到的是每个点到启示点的最短距离。

所以就导致了再dijastra中没有第65行代码(dis[]数组就是所求),另外在对dis数组进行更新的是有用dis[i]=min{dis[i],dis[u]+map[u][i]};

有兴趣的读者可以百度或google以下dijastra算法。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: