朱刘算法 && POJ3164 Command Network

问题描述:求最小树形图的总权值。即以固定根为起点,沿给定有向边,可以访问到所有的点,并使所构成的边权值之和最小,求这个最小总权值。

算法步骤:

①   清除自环,输入的时候判断即可

②   先判断从固定根开始是否可达所有原图中的点。简单搜索加标记位就可以。如果不可就不用说了,肯定没戏。

③   为除根之外的每个点选定一条最小入边。
(记pre [vi]为该边的起点)

④   判断这个入边集是否存在有向环,如果不存在,我们很容易证明这个集合就是该图的最小树形图,转⑥,否则接⑤。
(利用prev数组,枚举为检查过的点作为搜索的起点,做类似DFS的操作)

⑤   消环。设(u,i,w)表示从u到i的权为w的边。设刚才的有向环缩为新结点new。若u位于环上,并设环中指向u的边权是in[u]。那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。重复③④⑤

⑥   成功,返回DMST总权值。

补充1:如果无固定根,增加一个节点,连接到所有节点,并且距离一样,即可转化为有固定根。
补充2:本算法只能求最小总权值,但不能求路径。

算法实现:

例:POJ3164 Command Network

View Code
  1 #include <iostream>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <iomanip>
  5 #include <cstdio>
  6 using namespace std;
  7 #define INF 999999999
  8 #define MIN(x, y) ((x) < (y) ? (x) : (y))
  9 int villageX[101], villageY[101];
 10 double dis[101][101];
 11 bool exist[101];
 12 bool vis[101];//for DFS, judge if visited
 13 int pre[101];
 14 int nv, ne;//number of vertexs and edges
 15 
 16 void init();
 17 void solve();
 18 double zhuliu();
 19 double dist(int a, int b);
 20 void dfs(int t);
 21 
 22 int main(void)
 23 {
 24     //freopen("E:\\1.txt","r",stdin); 
 25     while (1)
 26     {
 27         init();
 28         if (!(cin >> nv >> ne)) break;
 29         for (int i = 1; i <= nv; i++)
 30             cin >> villageX[i] >> villageY[i];
 31         for (int i = 1; i <= ne; i++)
 32         {
 33             int a, b;
 34             cin >> a >> b;
 35             if (a != b && b != 1)//ensure no self-loop
 36                 dis[a][b] = dist(a, b);
 37         }
 38         solve();
 39     }
 40     //system("pause");
 41     return 0;
 42 }
 43 
 44 void init()
 45 {
 46     for (int i = 1; i <= 100; i++)
 47         for (int j = 1; j <= 100; j++)
 48             dis[i][j] = INF;
 49     for (int i = 1; i <= 100; i++)
 50         exist[i] = true;
 51     memset(villageX, 0, sizeof(villageX));
 52     memset(villageY, 0, sizeof(villageY));
 53     memset(vis, 0, sizeof(vis));
 54 }
 55 void solve()
 56 {
 57     double t = zhuliu();
 58     if (t < 0.0)
 59         cout << "poor snoopy" << endl;
 60     else
 61         cout << setprecision(2) << setiosflags(ios::fixed) << t << endl;
 62 }
 63 double zhuliu()
 64 {
 65     double res = 0.0;
 66     //judge possiblity
 67     dfs(1);
 68     for (int i = 1; i <= nv; i++)
 69         if (!vis[i]) return -1.0;
 70     while (1)
 71     {
 72         //search for minimum previous edge
 73         for (int i = 2; i <= nv; i++)
 74         {
 75             if (!exist[i]) continue;
 76             double min = INF;
 77             for (int j = 1; j <= nv; j++)
 78                 if (exist[j] && dis[j][i] < min)
 79                 {
 80                     min = dis[j][i];
 81                     pre[i] = j;
 82                 }
 83         }
 84         //search for loop
 85         int loopEntry = 0;
 86         for (int i = 2; i <= nv; i++)
 87         {
 88             int j = i;
 89             int count = 0;
 90             if (!exist[i]) continue;
 91             while (count <= nv)
 92             {
 93                 count++;
 94                 j = pre[j];
 95                 if (j == i)    {loopEntry = i; break;}
 96                 if (j == 1) break;
 97             }
 98             if (loopEntry) break;
 99         }
100         if (!loopEntry)//no loop any more
101         {
102             for (int i = 2; i <= nv; i++)
103                 if (exist[i])
104                     res += dis[pre[i]][i];
105             return res;
106         }
107         //delete loop
108         memset(vis, 0, sizeof(vis));
109         int k = loopEntry;
110         do
111         {
112             res += dis[pre[k]][k];
113             exist[k] = false;
114             vis[k] = true;
115             k = pre[k];
116         }while (k != loopEntry);
117         exist[loopEntry] = true;
118         for (int i = 1; i <= nv; i++) 
119             if (vis[i])//in the loop
120                 for (int j = 1; j <= nv; j++)
121                  if (!vis[j])//out of the loop
122                  {
123                      if (dis[i][j] != INF)
124                          dis[loopEntry][j] = MIN(dis[loopEntry][j], dis[i][j]);
125                      if (dis[j][i] != INF)
126                         dis[j][loopEntry] = MIN(dis[j][loopEntry], dis[j][i] - dis[pre[i]][i]);
127                  }
128     }
129 }
130 double dist(int a, int b)//distance
131 {
132     double dx = villageX[a] - villageX[b];
133     double dy = villageY[a] - villageY[b];
134     return sqrt(dx*dx + dy*dy);
135 }
136 void dfs(int t)
137 {
138     vis[t] = true;
139     for (int i = 1; i <= nv; i++)
140         if (dis[t][i] < INF && !vis[i])
141             dfs(i);
142 }

 

算法复杂度:O(VE)

posted on 2012-12-12 12:54  澄哥  阅读(1261)  评论(0)    收藏  举报

导航