题目链接:
http://poj.org/problem?id=3164
题目大意:
有n个点(用坐标表示)各点编号分别为1—>n,m条单向路,问能否存在一个花费价值最小的网络,能使从1点到达任一个点。
解题思路:
很明显的朱刘模板题,但是刚看到这个题目的时候,弱还不懂这个东西%>_<%
最小树形图就是给有向带权图中指定一个特殊的点root,求一棵以root为根的有向生成树T,并且T中所有边的总权值最小。
朱刘算法:当有向图中不存在自环,为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。所有的最小入边都选择出来了,如果这个入边集不存在有向环的话,这个集合就是该图的最小树形图。如果有自环的话就要消除自环,比如,现在我们假设分别有3-->1,权值为1 . 1-->3,权值为2 . 2-->1,权值为6 . 4-->3,权值为5. ,root为点4,存在自环1-->3-->1,现在就需要把1,3缩成一个点,则需要把4-->3边的权值5-2,2-->1边的权值变为6-1,。
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 const int maxn = 110; 8 const int N = 10010; 9 const double Exp = 1e-10; 10 const int INF = 0x3f3f3f3f; 11 struct Point 12 { 13 double x, y; 14 double length(Point a) 15 { 16 return sqrt((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y)); 17 } 18 }; 19 20 struct Edge 21 { 22 int u, v; 23 double w; 24 }; 25 26 Point point[maxn]; 27 Edge edge[N]; 28 int n, m; 29 double directed_MST (int root) 30 { 31 double pre[maxn], ans = 0; 32 //pre[i]为到i点权值最小的边值 33 int vis[maxn], id[maxn], pr[maxn]; 34 //vis[i]为i点是否在当前的数上,id[i]为i点是否在自环内, 35 int u, v; 36 while (true) 37 { 38 for (int i=1; i<=n; i++) 39 pre[i] = INF; 40 for (int i=0; i<m; i++) 41 { 42 u = edge[i].u; 43 v = edge[i].v; 44 if (edge[i].w<pre[v] && u!=v) 45 { 46 pre[v] = edge[i].w; 47 pr[v] = u;//到v点前驱 48 } 49 } 50 for (int i=1; i<=n; i++) 51 { 52 if (i == root) 53 continue; 54 if (pre[i] == INF) 55 return -1;//没有点与i相连,不存在最小树形图 56 } 57 memset (vis, -1, sizeof(vis)); 58 memset (id, -1, sizeof(id)); 59 int cru = 1;//对现存的点从新编号 60 pre[root] = 0; 61 for (int i=1; i<=n; i++) 62 { 63 ans += pre[i]; 64 v = i; 65 while (vis[v]!=i && id[v]==-1 && v!=root) 66 {//向树上加点 67 vis[v] = i; 68 v = pr[v]; 69 } 70 if (v!=root && id[v]==-1) 71 {//存在自环,并且把环变成一个点 72 for (u=pr[v]; u!=v; u=pr[u]) 73 id[u] = cru; 74 id[u] = cru ++; 75 } 76 } 77 if (cru == 1) 78 break;//不存在自环 79 for (int i=1; i<=n; i++) 80 if (id[i] == -1) 81 id[i] = cru ++;//对不在自环上的点编号 82 for (int i=0; i<m; i++) 83 {//把自环缩成一个点后,需要改变边的权 84 u = edge[i].u; 85 v = edge[i].v; 86 edge[i].u = id[u]; 87 edge[i].v = id[v]; 88 if (id[u] != id[v]) 89 edge[i].w -= pre[v]; 90 }//缩点后点数改变,根节点改变 91 n = cru - 1; 92 root = id[root]; 93 } 94 return ans; 95 } 96 int main () 97 { 98 while (scanf ("%d %d", &n, &m) != EOF) 99 { 100 for (int i=1; i<=n; i++) 101 scanf ("%lf %lf", &point[i].x, &point[i].y); 102 103 for (int i=0; i<m; i++) 104 { 105 int u, v; 106 scanf ("%d %d", &u, &v); 107 edge[i].u = u; 108 edge[i].v = v; 109 if (u == v) 110 edge[i].w = INF; 111 else 112 edge[i].w = point[u].length(point[v]); 113 } 114 double num = directed_MST(1); 115 if (num+1< Exp) 116 printf ("poor snoopy "); 117 else 118 printf ("%.2f ", num); 119 } 120 return 0; 121 }