将男女之间的关系看作一条边,想要费用最少就是找出一个最小生成树。
将边从小到大排序后,利用并查集将未连结的两点相连,便是最小生成树。
由于费用减免只能用于一人,我们将两人之间的费用减免(-d)作为边的权值。
求出最小生成树后将所有人的费用减去最大的所有的费用减免即是最小的费用。
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 20000 + 10;
const int maxr = 50000 + 10;
int par[maxn];
int ran[maxn];
void init(int n)
{
for (int i = 0; i <= n; i++) {
par[i] = i;
ran[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 (ran[x] < ran[y]) {
par[x] = y;
}
else {
par[y] = x;
if (ran[x] == ran[y]) {
ran[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
struct edge
{
int u, v, cost;
bool operator < (const edge& e) const {
return cost < e.cost;
}
};
edge es[maxr];
int Kruskal(int v, int e)
{
sort(es, es + e);
init(v);
int res = 0;
for (int i = 0; i < e; i++) {
edge ed = es[i];
if (!same(ed.u, ed.v)) {
unite(ed.u, ed.v);
res += ed.cost;
}
}
return res;
}
int main()
{
int t;
scanf("%d", &t);
while (t--) {
int n, m, r;
scanf("%d%d%d", &n, &m, &r);
for (int i = 0; i < r; i++) {
int x, y, d;
scanf("%d%d%d", &x, &y, &d);
es[i] = {x, n + y, -d};
}
printf("%d\n", 10000 * (n + m) + Kruskal(n + m, r));
}
return 0;
}