poj 3164 Command Network (朱刘算法)

题目链接:

  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\n");
117         else
118             printf ("%.2f\n", num);
119     }
120     return 0;
121 }

 

你可能感兴趣的:(command)