3 1 0 1 1 4 4 0 1 10 0 2 10 1 3 20 2 3 30
impossible 40 0
题解:求不定根的最小树形图实际上可以虚拟出一个根节点,它到原图的任意一点都有一条单向边,权值为原图的总权值+1==maxval,然后求以此虚拟节点为根的最小树形图,若最后求得的权值>=2*maxval说明至少有两个实际点没有入边,即最小树形图不存在。至于求编号最小的实际根节点,由于实际根节点必定跟虚拟节点相连,只需找到第一个跟虚拟点相连的实际点即可。倘若原图形成了一个环,如:
3 3
0 1 1
1 2 1
2 0 1
此时,由于环在进行缩点后会重新寻找最短入边集,此时前三条边形成自环,ans = 3;第4条边使得ansNum被标记为3,in[0] = 3,由于无环,ans += 3, ans == 6,算法结束,最终结果ans= 6 - 4 = 2, ansNum = 3 - 3 == 0。
更新后:
#include <stdio.h> #include <limits.h> #include <string.h> #define maxn 1002 #define maxm 11002 struct Node{ int u, v, cost; } E[maxm]; int in[maxn], vis[maxn], pre[maxn], id[maxn]; int ans, ansNum; void ZL_MST(int root, int nv, int ne) { ans = 0; int cnt, u, v, i; while(true){ for(i = 0; i < nv; ++i) in[i] = INT_MAX; for(i = 0; i < ne; ++i){ u = E[i].u; v = E[i].v; if(E[i].cost < in[v] && u != v){ in[v] = E[i].cost; pre[v] = u; if(u == root) ansNum = i; } } //检查是否有无入边的非根点可以省略 memset(vis, -1, sizeof(vis)); memset(id, -1, sizeof(id)); cnt = in[root] = 0; for(i = 0; i < nv; ++i){ ans += in[i]; v = i; while(vis[v] != i && v != root && id[v] == -1){ vis[v] = i; v = pre[v]; } if(v != root && id[v] == -1){ for(u = pre[v]; u != v; u = pre[u]) id[u] = cnt; id[v] = cnt++; } } if(cnt == 0) return; for(i = 0; i < nv; ++i) if(id[i] == -1) id[i] = cnt++; for(i = 0; i < ne; ++i){ v = E[i].v; E[i].u = id[E[i].u]; E[i].v = id[E[i].v]; if(E[i].u != E[i].v) E[i].cost -= in[v]; } nv = cnt; root = id[root]; } } int main() { int n, m, a, b, c, i, tmp; while(scanf("%d%d", &n, &m) == 2){ for(i = tmp = 0; i < m; ++i){ scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].cost); tmp += E[i].cost; } ++tmp; for(i = 0; i < n; ++i){ E[m+i].u = n; E[m+i].v = i; E[m+i].cost = tmp; } ZL_MST(n, n + 1, m + n); if(ans >= 2 * tmp) puts("impossible\n"); else printf("%d %d\n\n", ans - tmp, ansNum - m); } return 0; }
#include <stdio.h> #include <limits.h> #include <string.h> #define maxn 1002 #define maxm 12000 struct Node{ int u, v, cost; } E[maxm]; int pre[maxn], vis[maxn], in[maxn], hash[maxn], ansNum; int ans; bool ZL_MST(int root, int nv, int ne) { ans = 0; int i, u, v, cntnum, tmp; while(true){ for(i = 0; i < nv; ++i) in[i] = INT_MAX; for(i = tmp = 0; i < ne; ++i){ u = E[i].u; v = E[i].v; if(E[i].cost < in[v] && v != u){ in[v] = E[i].cost; pre[v] = u; if(u == root) ansNum = i; } } in[root] = 0; for(i = 0; i < nv; ++i) if(in[i] == INT_MAX) return false; cntnum = 0; memset(vis, -1, sizeof(vis)); memset(hash, -1, sizeof(hash)); for(i = 0; i < nv; ++i){ ans += in[i]; v = i; while(vis[v] != i && v != root && hash[v] == -1){ vis[v] = i; v = pre[v]; } if(v != root && hash[v] == -1){ for(u = pre[v]; u != v; u = pre[u]) hash[u] = cntnum; hash[v] = cntnum++; } } if(cntnum == 0) return true; for(i = 0; i < nv; ++i) if(hash[i] == -1) hash[i] = cntnum++; for(i = 0; i < ne; ++i){ v = E[i].v; E[i].u = hash[E[i].u]; E[i].v = hash[E[i].v]; if(E[i].u != E[i].v) E[i].cost -= in[v]; } nv = cntnum; root = hash[root]; } return true; } int main() { int n, m, a, b, c, i, maxVal; while(scanf("%d%d", &n, &m) == 2){ for(i = maxVal = 0; i < m; ++i){ scanf("%d%d%d", &a, &b, &c); E[i].u = a; E[i].v = b; E[i].cost = c; maxVal += c; } //添加虚拟节点以及n条虚拟边 for(++maxVal, i = 0; i < n; ++i){ E[m+i].u = n; E[m+i].v = i; E[m+i].cost = maxVal; } if(!ZL_MST(n, n + 1, n + m) || ans >= 2 * maxVal) printf("impossible\n\n"); else printf("%d %d\n\n", ans - maxVal, ansNum - m); } return 0; }