HDU2121 Ice_cream’s world II —— 最小树形图 + 不定根 + 超级点
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121
Ice_cream’s world II
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5832 Accepted Submission(s): 1493
题解:
1.题目要求:给定一幅有向图,求最小树形图(根节点不确定)。
2.一开始想枚举每个结点作为根节点,然后跑zhuliu算法,求出最小值。结果发现复杂度太大。
3.可行做法:设置一个超级点,作为虚拟的根节点,把超级点连向每一个题目中的点。然后跑zhuliu算法,如果所得的最小树形图中只有一条超级边(超级点连向题目中的点,这个点就是实际的根节点),那么就求出实际了最小树形图;如果有多条超级边(实际得到的为最小树形图森林),则无解。
4.那么超级边的权值应该设为多少呢?由于我们需要从zhuliu算法返回的数据中判断出有多少条超级边,所以超级边就应该设置的足够大,以方便检测,但又不能溢出。所以我们将其设置为题目中所有边的权值之和+1。这样,只要zhuliu()返回来的数据:ans<2*super_edge,就表明只含有一条超级边,所以最终答案为ans-super_edge(减去人工设置的超级边)。否则,如果ans>=2*super_edge,则表明至少有两条超级边,也就说明了:在实际的图中(没有超级点),至少有两个结点是没有入边的。然而没有入边的结点只能有1个或者没有(作为根节点),所以无解。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 using namespace std; 7 typedef long long LL; 8 const double EPS = 1e-6; 9 const int INF = INT_MAX; 10 const LL LNF = 9e18; 11 const int MOD = 1e9+7; 12 const int MAXN = 1e3+10; 13 14 struct Edge 15 { 16 int u, v, w; 17 }edge[10010]; 18 19 //super_edge为超级点连向每个普通点的边权, root_pos用于记录实际的根节点。 20 int super_edge, root_pos; 21 int pre[MAXN], id[MAXN], vis[MAXN], in[MAXN]; 22 23 int zhuliu(int root, int n, int m) 24 { 25 int res = 0; 26 while(1) 27 { 28 for(int i = 0; i<n; i++) 29 in[i] = INF; 30 for(int i = 0; i<m; i++) 31 if(edge[i].u!=edge[i].v && edge[i].w<in[edge[i].v]) 32 { 33 pre[edge[i].v] = edge[i].u; 34 in[edge[i].v] = edge[i].w; 35 //为什么可以这样记录实际的根节点呢?因为在main()函数中,我们设置超级点连向普通点的时候, 36 //边的下标从m开始,对应着结点0, m+1对应着结点1,………所以我们可以根据边的下标得出边的终点。 37 if(edge[i].u==root) 38 root_pos = i; 39 40 } 41 42 for(int i = 0; i<n; i++) 43 if(i!=root && in[i]==INF) 44 return -1; 45 46 int tn = 0; 47 memset(id, -1, sizeof(id)); 48 memset(vis, -1, sizeof(vis)); 49 in[root] = 0; 50 for(int i = 0; i<n; i++) 51 { 52 res += in[i]; 53 int v = i; 54 while(vis[v]!=i && id[v]==-1 && v!=root) 55 { 56 vis[v] = i; 57 v = pre[v]; 58 } 59 if(v!=root && id[v]==-1) 60 { 61 for(int u = pre[v]; u!=v; u = pre[u]) 62 id[u] = tn; 63 id[v] = tn++; 64 } 65 } 66 if(tn==0) break; 67 for(int i = 0; i<n; i++) 68 if(id[i]==-1) 69 id[i] = tn++; 70 71 for(int i = 0; i<m; i++) 72 { 73 int v = edge[i].v; 74 edge[i].u = id[edge[i].u]; 75 edge[i].v = id[edge[i].v]; 76 if(edge[i].u!=edge[i].v) 77 edge[i].w -= in[v]; 78 } 79 n = tn; 80 root = id[root]; 81 } 82 return res; 83 } 84 85 int main() 86 { 87 int n, m; 88 while(scanf("%d%d", &n, &m)!=EOF) 89 { 90 super_edge = 0; 91 for(int i = 0; i<m; i++) 92 { 93 scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w); 94 super_edge += edge[i].w; 95 } 96 97 super_edge++; 98 for(int i = 0; i<n; i++) //n为超级点,将超级点连向每一个题目中的点 99 { 100 edge[m+i].u = n; 101 edge[m+i].v = i; 102 edge[m+i].w = super_edge; 103 } 104 105 int ans = zhuliu(n, n+1, m+n); 106 if(ans==-1 || ans>=2*super_edge) printf("impossible\n\n"); 107 else printf("%d %d\n\n", ans-super_edge, root_pos-m); 108 } 109 }