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

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

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

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算法。。。

posted @ 2012-04-29 12:51  Amazing_Y  阅读(430)  评论(0编辑  收藏  举报