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代码:
最后我还想将prim与dijastra算法最下对比:
它两的代码很相似
prim得到的是最小生成树,最短连通路径,
而dijastra得到的是每个点到启示点的最短距离。
所以就导致了再dijastra中没有第65行代码(dis[]数组就是所求),另外在对dis数组进行更新的是有用dis[i]=min{dis[i],dis[u]+map[u][i]};
有兴趣的读者可以百度或google以下dijastra算法。。。
好了废话不多说了!~开始我的题目。
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算法。。。