朱刘算法 && 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)

浙公网安备 33010602011771号