http://poj.org/problem?id=3723
题目的意思相当于是给出一个非连通图,非连通图可以看成是几个连通图的组合。每一次有人入伍的时候,都会和他/她所在的连通图中,和已经入伍的人中最亲密的那个连一条边,最后每一个连通图中都会有一棵树,因为所有人都入伍了,并且要求边长和最长的,那么就是一棵最大生成树。随便来一棵包含该非连通图全部结点的树,一定也是相对应几种入伍方式,一种入伍方式一定对应一棵包含全部结点的树。由于是非连通图,要求每个连通图的最大生成树所有权重之和,所以只能用kruskal,prim只能用于一个连通图的计算。
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<iostream>
#define N 50005
using namespace std;
struct edge
{
int u, v, cost;
}es[N];
int par[N], Rank[N], x, y, p, n, m, r, ans, T;
bool cmp(edge a, edge b)
{
return a.cost > b.cost;
}
void init_union_find(int n)
{
for (int i = 0; i < n; i++)
{
par[i] = i;
Rank[i] = 0;
}
}
int Find(int x)
{
if (par[x] == x)
{
return x;
}
else
{
return par[x] = Find(par[x]);
}
}
void unite(int x, int y)
{
x = Find(x);
y = Find(y);
if (x == y) return;
if (Rank[x] < Rank[y])
{
par[x] = y;
}
else
{
par[y] = x;
if (Rank[x] == Rank[y]) Rank[x]++;
}
}
bool same(int x, int y)
{
return Find(x) == Find(y);
}
int kruskal()
{
sort(es, es+r, cmp);
init_union_find(n+m);
int res = 0;
for (int i = 0; i < r; i++)
{
edge e = es[i];
if (!same(e.u, e.v))
{
unite(e.u, e.v);
res += e.cost;
}
}
return res;
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d%d%d", &n, &m, &r);
for (int i = 0; i < r; i++)
{
scanf("%d%d%d", &x, &y, &p);
es[i].u = x;
es[i].v = y+n;
es[i].cost = p;
}
ans = 10000*(n+m) - kruskal();
printf("%d\n", ans);
}
return 0;
}