题目: 传送门
思路:
把人看成点,征募花费看作边,那么就有一个无向图,因为当x和y关系是d时,在已招募x的情况下招募y的花费是10000-d,最小化花费等价于最大化d,故就是求“最大支撑树”(有V个节点,V-1条边,且边权重和最大),进而把d保存为-d,就转换成求无向图的最小生成树问题,直接套模板解决,注意,最后要用10000*V(人数)-最小生成树的边权和
Code:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int inf = 0x3f3f3f3f;
const int maxn = 100007;
struct node {
int from, to, dist;
bool operator < (const node& other) const {
return dist > other.dist;
}
};
int n, m, v, e;
priority_queue<node> q;
int fa[maxn];
int rk[maxn];
void init() {
for (int i=0;i<=v;i++) {
fa[i] = i;
rk[i] = 0;
while (!q.empty()) {
q.pop();
}
}
}
int find_root(int x) {
return x == fa[x] ? x : fa[x] = find_root(fa[x]);
}
void merge(int x, int y) {
x = find_root(x);
y = find_root(y);
if (x == y) return;
if (rk[x] < rk[y]) {
fa[x] = y;
} else {
fa[y] = x;
if (rk[x] == rk[y]) rk[x]++;
}
}
int main()
{
int f, t, d;
int T;
scanf("%d", &T);
while (T--) {
scanf("%d %d %d", &n, &m, &e);
v = n + m;
init();
for (int i=0;i<e;i++) {
scanf("%d %d %d", &f, &t, &d);
q.push(node {
f, n+t, -d});
q.push(node {
n+t, f, -d});
}
int in_num = 0;
ll ans = 10000*v;
while (!q.empty()) {
if (in_num == v-1) break;
node tp = q.top();q.pop();
if (find_root(tp.from) != find_root(tp.to)) {
ans += tp.dist;
in_num++;
merge(tp.from, tp.to);
}
}
printf("%lld\n", ans);
}
return 0;
}