模板题:
/***********************************\
* @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 */
/*
最小树形图模板题,不多说。
*/
/**********************************\
* @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 */
/*
最小树形图模板题,不多说。
*/
/************************************\
* @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 */
/*
最小树形图模板题,不多说。
*/
无定根:/****************************************\
* @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类型。
*/
(这并不是一道真正意义上的最小树形图:)
/***************************************\
* @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 */
/*
缩点,然后将所有最小入边相加。
*/
稍加变形:
/**********************************\
* @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为附加费用,而不是全部费用)。
*/