最小树形图:朱刘算法

给一个有向图,求最小树形图的权值。

用in存图,用pre记录前驱节点。

  1 #include<iostream>
  2 using namespace std;
  3 #include<cstdio>
  4 #include<cstring>
  5 #define MAXN 1005
  6 #define INF 0x7f7f7f7f
  7 typedef __int64 type;
  8 struct node//边的权和顶点
  9 {
 10     int u, v;
 11     type w;
 12 }edge[MAXN * MAXN];
 13 int pre[MAXN], id[MAXN], vis[MAXN], n, m, pos;
 14 type in[MAXN];//存最小入边权,pre[v]为该边的起点
 15 type Directed_MST(int root, int V, int E)
 16 {
 17     type ret = 0;//存最小树形图总权值
 18     while(true)
 19     {
 20         int i;
 21         //1.找每个节点的最小入边
 22         for( i = 0; i < V; i++)
 23             in[i] = INF;//初始化为无穷大
 24         for( i = 0; i < E; i++)//遍历每条边
 25         {
 26             int u = edge[i].u;
 27             int v = edge[i].v;
 28             if(edge[i].w < in[v] && u != v)//说明顶点v有条权值较小的入边  记录之
 29             {
 30                 pre[v] = u;//节点u指向v
 31                 in[v] = edge[i].w;//最小入边
 32                 if(u == root)//这个点就是实际的起点
 33                     pos = i;
 34             }
 35         }
 36         for( i = 0; i < V; i++)//判断是否存在最小树形图
 37         {
 38             if(i == root)
 39                 continue;
 40             if(in[i] == INF)
 41                 return -1;//除了根以外有点没有入边,则根无法到达它  说明它是独立的点 一定不能构成树形图
 42         }
 43         //2.找环
 44         int cnt = 0;//记录环数
 45         memset(id, -1, sizeof(id));
 46         memset(vis, -1, sizeof(vis));
 47         in[root] = 0;
 48         for( i = 0; i < V; i++) //标记每个环
 49         {
 50             ret += in[i];//记录权值
 51             int v = i;
 52             while(vis[v] != i && id[v] == -1 && v != root)
 53             {
 54                 vis[v] = i;
 55                 v = pre[v];
 56             }
 57             if(v != root && id[v] == -1)
 58             {
 59                 for(int u = pre[v]; u != v; u = pre[u])
 60                     id[u] = cnt;//标记节点u为第几个环
 61                 id[v] = cnt++;
 62             }
 63         }
 64         if(cnt == 0)
 65             break; //无环   则break
 66         for( i = 0; i < V; i++)
 67             if(id[i] == -1)
 68                 id[i] = cnt++;
 69             //3.建立新图   缩点,重新标记
 70             for( i = 0; i < E; i++)
 71             {
 72                 int u = edge[i].u;
 73                 int v = edge[i].v;
 74                 edge[i].u = id[u];
 75                 edge[i].v = id[v];
 76                 if(id[u] != id[v])
 77                     edge[i].w -= in[v];
 78             }
 79             V = cnt;
 80             root = id[root];
 81     }
 82     return ret;
 83 }
 84 int main()
 85 {
 86     int i;
 87     while(scanf("%d%d", &n, &m) != EOF)
 88     {
 89         type sum = 0;
 90         for( i = 0; i < m; i++)
 91         {
 92             scanf("%d%d%I64d", &edge[i].u, &edge[i].v, &edge[i].w);
 93             edge[i].u++; edge[i].v++;
 94             sum += edge[i].w;
 95         }
 96       sum ++;
 97         for( i = m; i < m + n; i++)//增加超级节点0,节点0到其余各个节点的边权相同(此题中 边权要大于原图的总边权值)
 98         {
 99             edge[i].u = 0;
100             edge[i].v = i - m + 1;
101             edge[i].w = sum;
102         }
103         type ans = Directed_MST(0, n + 1, m + n);
104         //n+1为总结点数,m+n为总边数
105         //ans代表以超级节点0为根的最小树形图的总权值,
106         //将ans减去sum,如果差值小于sum,说明节点0的出度只有1,说明原图是连通图
107         //如果差值>=sum,那么说明节点0的出度不止为1,说明原图不是连通图
108         if(ans == -1 || ans - sum >= sum)
109             puts("impossible");
110         else
111             printf("%I64d %d
",ans - sum, pos - m);
112         puts("");
113     }
114     return 0;
115 }
原文地址:https://www.cnblogs.com/St-Lovaer/p/12008216.html