今天是我在自己的博客里发表的第一篇文章, 希望以后能把自己的博客越办越好 ,也希望和大家一起讨论,研究,成长进步 。
好了废话不多说了!~开始我的题目。
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代码:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #define MAX 9999999 5 using namespace std; 6 7 int map[55][55]; //用来表示图中的边 没有边的点之间置为0 8 int hash[55]; // 判断节点是否在u0中 9 int dis[55]; // 用来表示节点到集合u0的最短距离,无法到达置为很大的数 10 int N,M; 11 12 int prim(); 13 14 int main(void) 15 { 16 int a,b,c; 17 while(scanf("%d",&N)!=EOF&&N) 18 { 19 getchar(); 20 scanf("%d",&M); 21 getchar(); 22 memset(map,0,sizeof(map)); 23 memset(hash,0,sizeof(hash)); 24 memset(dis,0,sizeof(dis)); 25 for(int i=0;i<M;i++) 26 { 27 scanf("%d%d%d",&a,&b,&c); 28 if(map[a][b]==0||map[a][b]>c) 29 { 30 map[a][b]=c; 31 map[b][a]=c; 32 } 33 } 34 printf("%d\n",prim()); 35 } 36 // system("pause"); 37 return 0; 38 } 39 40 int prim() 41 { 42 int sum=0; 43 for(int i=2;i<=N;i++) 44 { 45 dis[i]=MAX; 46 if(map[1][i]) 47 { 48 dis[i]=map[1][i]; 49 } 50 } 51 hash[1]=1; 52 dis[1]=0; 53 for(int k=2;k<=N;k++) //进行N-1次循环 54 { 55 int min=MAX,u=1; 56 for(int i=2;i<=N;i++) //找距离u0最近的点 57 { 58 if(min>dis[i]&&!hash[i]) 59 { 60 min=dis[i]; 61 u=i; 62 } 63 } 64 hash[u]=1; 65 sum+=min; 66 for(int i=2;i<=N;i++) //更新节点到u0的距离 67 { 68 if(map[u][i]&&dis[i]>map[u][i]&&!hash[i]) //当i与u之间有边 dis[i]=min{dis[i],map[u][i]} 69 { 70 dis[i]=map[u][i]; 71 } 72 } 73 } 74 return sum; 75 }
最后我还想将prim与dijastra算法最下对比:
它两的代码很相似
prim得到的是最小生成树,最短连通路径,
而dijastra得到的是每个点到启示点的最短距离。
所以就导致了再dijastra中没有第65行代码(dis[]数组就是所求),另外在对dis数组进行更新的是有用dis[i]=min{dis[i],dis[u]+map[u][i]};
有兴趣的读者可以百度或google以下dijastra算法。。。