模板题:
poj3164 Command Network
/***********************************\ * @prob: poj3164 Command Network * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 25th, 2012 * * @memo: 最小树形图 * \***********************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> #include <cmath> const int maxN = 110; const double INF = 1e198; double mp[maxN][maxN], x[maxN], y[maxN]; bool del[maxN]; int pre[maxN], root, n, m; template <typename _Tp> inline _Tp& gmax(_Tp& a, const _Tp& b) {return a > b ? a : (a = b);} /* gmax */ template <typename _Tp> inline _Tp& gmin(_Tp& a, const _Tp& b) {return a < b ? a : (a = b);} /* gmin */ template <typename _Tp> inline _Tp sqr(const _Tp& x) {return x * x;} /* sqr */ inline bool connected() { static const int SIZE = 0x3ffff; static int q[SIZE + 1]; static bool marked[maxN]; memset(marked, 0, sizeof marked); int f = 0, r = 0, u, v; for (q[r++] = 0; f - r;) for (marked[u = q[f++]] = true, f &= SIZE, v = 0; v < n; ++v) if (mp[u][v] < INF && !marked[v]) marked[q[r++] = v] = true, r &= SIZE; for (int i = 0; i < n; ++i) if (!marked[i]) return false; return true; } /* connected */ inline int loop() { pre[root = 0] = 0; for (int i = 0; i < n; ++i) if (!del[i] && i != root) { mp[i][i] = INF; pre[i] = i; for (int j = 0; j < n; ++j) if (!del[j] && mp[j][i] < mp[pre[i]][i]) pre[i] = j; } /* if */ for (int i = 0; i < n; ++i) if (!del[i] && i != root) { static bool vis[maxN]; memset(vis, 0, sizeof vis); int j = i; for (; !vis[j]; j = pre[j]) vis[j] = true; if (j == root) continue; else return j; } /* if */ return -1; } /* loop */ inline void update(int t, double& ans) { ans += mp[pre[t]][t]; for (int i = pre[t]; i != t; i = pre[i]) ans += mp[pre[i]][i], del[i] = true; for (int i = 0; i < n; ++i) if (!del[i] && mp[i][t] < INF) mp[i][t] -= mp[pre[t]][t]; for (int j = pre[t]; j != t; j = pre[j]) for (int i = 0; i < n; ++i) if (!del[i]) { if (mp[i][j] < INF) gmin(mp[i][t], mp[i][j] - mp[pre[j]][j]); if (mp[j][i] < INF) gmin(mp[t][i], mp[j][i]); } /* for */ return; } /* update */ inline double solve() { memset(del, 0, sizeof del); double ans = 0; int j; while (~(j = loop())) update(j, ans); for (int i = 0; i < n; ++i) if (!del[i] && i != root) ans += mp[pre[i]][i]; return ans; } /* solve */ int main() { freopen("network.in" , "r", stdin ); freopen("network.out", "w", stdout); while (~scanf("%d%d", &n, &m)) { for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) mp[i][j] = INF; for (int i = 0; i < n; ++i) scanf("%lf%lf", x + i, y + i); while (m--) { int u, v; scanf("%d%d", &u, &v); --u, --v; mp[u][v] = sqrt(sqr(x[u] - x[v]) + sqr(y[u] - y[v])); } /* while */ if (!connected()) puts("poor snoopy"); else printf("%.2lf\n", solve()); } /* while */ return 0; } /* main */ /* 最小树形图模板题,不多说。 */
tju2248 Channel Design
/**********************************\ * @prob: tju2248 Channel Design * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 25th, 2012 * * @memo: 最小树形图 * \**********************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 110, maxM = 10010, INF = 0x3f3f3f3f; struct Edge { int u, v, d; Edge(int u = 0, int v = 0, int d = 0): u(u), v(v), d(d) {} /* Edge */ } edge[maxM]; int n, m; int Directed_MST(int root, int n, int m) { int res = 0; for (;;) { static int In[maxN], pre[maxN], ID[maxN], vis[maxN]; memset(In, 0x3f, sizeof In); for (int j = 0; j < m; ++j) { int u = edge[j].u, v = edge[j].v, d = edge[j].d; if (u != v && d < In[v]) In[v] = d, pre[v] = u; } /* for */ for (int i = 0; i < n; ++i) if (i != root && In[i] == INF) return -1; memset(vis, 0xff, sizeof vis); memset(ID , 0xff, sizeof ID ); int cnt_v = 0; for (int i = 0; i < n; ++i) if (i != root) { res += In[i]; int u = i; for (; vis[u] != i && ID[u] == -1 && u != root; u = pre[u]) vis[u] = i; if (u != root && ID[u] == -1) { for (int v = pre[u]; v != u; v = pre[v]) ID[v] = cnt_v; ID[u] = cnt_v++; } /* if */ } /* if */ if (!cnt_v) break; for (int i = 0; i < n; ++i) if (ID[i] == -1) ID[i] = cnt_v++; for (int j = 0; j < m; ++j) { int v = edge[j].v; edge[j].u = ID[edge[j].u]; edge[j].v = ID[edge[j].v]; if (edge[j].u != edge[j].v) edge[j].d -= In[v]; } /* for */ n = cnt_v; root = ID[root]; } /* for */ return res; } /* Directed_MST */ int main() { freopen("design.in" , "r", stdin ); freopen("design.out", "w", stdout); while (~scanf("%d%d", &n, &m) && (n || m)) { int cnt_e = 0; while (m--) { int u, v, d; scanf("%d%d%d", &u, &v, &d); edge[cnt_e++] = Edge(--u, --v, d); } /* while */ int ans = Directed_MST(0, n, cnt_e); if (ans < 0) puts("impossible"); else printf("%d\n", ans); } /* while */ return 0; } /* main */ /* 最小树形图模板题,不多说。 */
UVa11183 Teen Girl Squad
/************************************\ * @prob: UVa11183 Teen Girl Squad * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 25th, 2012 * * @memo: 最小树形图 * \************************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 1010, maxM = 40010, INF = 0x3f3f3f3f; struct Edge {int u, v, d;} edge[maxM]; int n, m, T; int Directed_MST(int root, int n, int m) { int res = 0; for (;;) { static int In[maxN], pre[maxN], ID[maxN], vis[maxN]; memset(In, 0x3f, sizeof In); for (int j = 0; j < m; ++j) { int u = edge[j].u, v = edge[j].v, d = edge[j].d; if (u != v && d < In[v]) In[v] = d, pre[v] = u; } /* for */ for (int i = 0; i < n; ++i) if (i != root && In[i] == INF) return -1; memset(ID , 0xff, sizeof ID ); memset(vis, 0xff, sizeof vis); int cnt_v = 0; for (int i = 0; i < n; ++i) if (i != root) { res += In[i]; int u = i; for (; vis[u] != i && ID[u] == -1 && u != root; u = pre[u]) vis[u] = i; if (ID[u] == -1 && u != root) { for (int v = pre[u]; v != u; v = pre[v]) ID[v] = cnt_v; ID[u] = cnt_v++; } /* if */ } /* if */ if (!cnt_v) break; for (int i = 0; i < n; ++i) if (ID[i] == -1) ID[i] = cnt_v++; for (int j = 0; j < m; ++j) { int v = edge[j].v; if ((edge[j].u = ID[edge[j].u]) != (edge[j].v = ID[edge[j].v])) edge[j].d -= In[v]; } /* for */ root = ID[root]; n = cnt_v; } /* for */ return res; } /* Directed_MST */ int main() { freopen("girl.in" , "r", stdin ); freopen("girl.out", "w", stdout); for (scanf("%d", &T); T--;) { scanf("%d%d", &n, &m); for (int i = 0; i < m; ++i) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].d); static int Case = 0; printf("Case #%d: ", ++Case); int ans = Directed_MST(0, n, m); if (ans < 0) puts("Possums!"); else printf("%d\n", ans); } /* for */ return 0; } /* main */ /* 最小树形图模板题,不多说。 */无定根:
hdu2121 Ice_cream’s world II
/****************************************\ * @prob: hdu2121 Ice_cream’s world II * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 25th, 2012 * * @memo: 最小树形图 * \****************************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 1010, maxM = 11010; typedef long long int64; int64 INF = 0x3f3f3f3f3f3f3f3fLL; struct Edge {int u, v; int64 d;} edge[maxM]; int n, m; int64 Directed_MST(int root, int n, int m, int& root_id) { int64 res = 0; for (;;) { static int pre[maxN], ID[maxN], vis[maxN]; static int64 In[maxN]; memset(In, 0x3f, sizeof In); for (int j = 0; j < m; ++j) { int u = edge[j].u, v = edge[j].v; int64 d = edge[j].d; if (u != v && d < In[v]) //!! { In[v] = d, pre[v] = u; if (u == root) root_id = j; } /* if */ } /* for */ for (int i = 0; i < n; ++i) if (i != root && In[i] == INF) return -1; memset(ID , 0xff, sizeof ID ); memset(vis, 0xff, sizeof vis); int cnt_v = 0; for (int i = 0; i < n; ++i) if (i != root) { res += In[i]; int u = i; for (; vis[u] != i && ID[u] == -1 && u != root; u = pre[u]) vis[u] = i; if (ID[u] == -1 && u != root) { for (int v = pre[u]; v != u; v = pre[v]) ID[v] = cnt_v; ID[u] = cnt_v++; } /* if */ } /* if */ if (!cnt_v) break; for (int i = 0; i < n; ++i) if (ID[i] == -1) ID[i] = cnt_v++; for (int j = 0; j < m; ++j) { int v = edge[j].v; if ((edge[j].u = ID[edge[j].u]) != (edge[j].v = ID[edge[j].v])) edge[j].d -= In[v]; } /* for */ root = ID[root]; n = cnt_v; } /* for */ return res; } /* Directed_MST */ int main() { freopen("Ice_cream.in" , "r", stdin ); freopen("Ice_cream.out", "w", stdout); while (~scanf("%d%d", &n, &m)) { int64 sum_d = 0; for (int i = 0; i < m; ++i) scanf("%d%d%lld", &edge[i].u, &edge[i].v, &edge[i].d), sum_d += edge[i].d; ++sum_d; int cnt_e = m, root_id = 0; for (int i = 0; i < n; ++i) edge[cnt_e].u = n, edge[cnt_e].v = i, edge[cnt_e++].d = sum_d; int64 ans = Directed_MST(n, n + 1, cnt_e, root_id) - sum_d; if (ans < 0 || ans >= sum_d) puts("impossible\n"); else printf("%lld %d\n\n", ans, root_id - m); } /* while */ return 0; } /* main */ /* 无定根的最小树形图。 只需要加一个虚拟根,到所有点的权值恰好比所有原边权值之和略大(这样保证虚拟根只会与其中一个点连边),并且再记录一下根的位置就可以了(如果一个点的入边为虚拟根,那么这个点为“真实的”根)。 要用long long类型。 */(这并不是一道真正意义上的最小树形图:)
hdu3072 Intelligence System
/***************************************\ * @prob: hdu3072 Intelligence System * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 25th, 2012 * * @memo: 强连通分量 * \***************************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> typedef long long int64; const int maxN = 50010, maxM = 100010; const int64 INF = 0x3f3f3f3f3f3f3f3fLL; struct Edge { int u, v; int64 d; Edge* next; Edge() {} /* Edge */ Edge(int u, int v, int64 d, Edge* next): u(u), v(v), d(d), next(next) {} /* Edge */ } __edge[maxM], *edge[maxN], *tot = __edge; bool marked[maxN]; int Low[maxN], DFN[maxN], sta[maxN]; int ID[maxN], n, m, Bcnt, Index, top; template <typename _Tp> inline _Tp& gmax(_Tp& a, const _Tp& b) {return a > b ? a : (a = b);} /* gmax */ template <typename _Tp> inline _Tp& gmin(_Tp& a, const _Tp& b) {return a < b ? a : (a = b);} /* gmin */ void Tarjan(int u) { DFN[u] = Low[u] = Index++; marked[sta[top++] = u] = true; for (Edge* p = edge[u]; p; p = p -> next) { if (DFN[p -> v] == -1) Tarjan(p -> v), gmin(Low[u], Low[p -> v]); else if (marked[p -> v]) gmin(Low[u], DFN[p -> v]); } /* for */ if (Low[u] == DFN[u]) { int tmp = u; do ID[tmp = sta[--top]] = Bcnt, marked[tmp] = false; while (tmp != u); ++Bcnt; } /* if */ return; } /* Tarjan */ int main() { freopen("Intelligence.in" , "r", stdin ); freopen("Intelligence.out", "w", stdout); while (~scanf("%d%d", &n, &m)) { memset(edge, 0, sizeof edge); tot = __edge; while (m--) { int u, v; int64 d; scanf("%d%d%lld", &u, &v, &d); edge[u] = new (tot++) Edge(u, v, d, edge[u]); } /* while */ memset(ID , 0xff, sizeof ID ); memset(DFN , 0xff, sizeof DFN ); memset(Low , 0xff, sizeof Low ); memset(marked, 0 , sizeof marked); Bcnt = top = Index = 0; Tarjan(0); static int64 In[maxN]; memset(In, 0x3f, sizeof In); for (Edge* p = __edge; p != tot; ++p) if (ID[p -> u] != ID[p -> v]) gmin(In[ID[p -> v]], p -> d); In[ID[0]] = 0; int64 ans = 0; for (int i = 0; i < Bcnt; ++i) ans += In[i]; printf("%lld\n", ans); } /* while */ return 0; } /* main */ /* 缩点,然后将所有最小入边相加。 */
稍加变形:
hdu4009 Transfer water
/**********************************\ * @prob: hdu4009 Transfer water * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 25th, 2012 * * @memo: 最小树形图 * \**********************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 1010, maxM = maxN * (maxN + 1), INF = 0x3f3f3f3f; struct Edge {int u, v, d;} edge[maxM]; int x[maxN], y[maxN], z[maxN], n, m, X, Y, Z, cnt_e; inline int dist(int i, int j) {return std::abs(x[i] - x[j]) + std::abs(y[i] - y[j]) + std::abs(z[i] - z[j]);} inline void Ins(int u, int v, int d) { edge[cnt_e].u = u; edge[cnt_e].v = v; edge[cnt_e].d = d; ++cnt_e; return; } /* Ins */ int Directed_MST(int root, int n, int m) { int res = 0; for (;;) { static int pre[maxN], ID[maxN], vis[maxN]; static int In[maxN]; memset(In, 0x3f, sizeof In); for (int j = 0; j < m; ++j) { int u = edge[j].u, v = edge[j].v; int d = edge[j].d; if (u != v && d < In[v]) In[v] = d, pre[v] = u; } /* for */ for (int i = 0; i < n; ++i) if (i != root && In[i] == INF) return -1; memset(ID , 0xff, sizeof ID ); memset(vis, 0xff, sizeof vis); int cnt_v = 0; for (int i = 0; i < n; ++i) if (i != root) { res += In[i]; int u = i; for (; ID[u] == -1 && u != root && vis[u] != i; u = pre[u]) vis[u] = i; if (u != root && ID[u] == -1) //!! { for (int v = pre[u]; v != u; v = pre[v]) ID[v] = cnt_v; ID[u] = cnt_v++; } /* if */ } /* if */ if (!cnt_v) break; for (int i = 0; i < n; ++i) if (ID[i] == -1) ID[i] = cnt_v++; //!! for (int j = 0; j < m; ++j) { int v = edge[j].v; if ((edge[j].u = ID[edge[j].u]) != (edge[j].v = ID[edge[j].v])) edge[j].d -= In[v]; } /* for */ n = cnt_v; root = ID[root]; } /* for */ return res; } /* Directed_MST */ int main() { freopen("Transfer.in" , "r", stdin ); freopen("Transfer.out", "w", stdout); while (~scanf("%d%d%d%d", &n, &X, &Y, &Z) && (n || X || Y || Z)) { for (int i = 0; i < n; ++i) scanf("%d%d%d", x + i, y + i, z + i); cnt_e = 0; for (int i = 0, K, j; i < n; ++i) { scanf("%d", &K); while (K--) { scanf("%d", &j); --j; Ins(i, j, Y * dist(i, j) + (z[i] < z[j] ? Z : 0)); } /* while */ } /* for */ for (int i = 0; i < n; ++i) Ins(n, i, z[i] * X); int ans = Directed_MST(n, n + 1, cnt_e); if (ans < 0) puts("poor XiaoA"); else printf("%d\n", ans); } /* while */ return 0; } /* main */ /* 新建虚拟根,虚拟根到每个点的权值为该点自建水库的费用。 注意当i的海拔低于j时,仍然要算上Y的基本费用(Z为附加费用,而不是全部费用)。 */