题目描述:现需招募男兵M人,女兵N人,每招募一人需花费10000美元,若已征募的人中有一些存在亲密关系,则可减少征募费用,征募费用=10000 - (与已征募的人中亲密度的最大值)。若使用恰当的征募顺序,可使得征募的总费用最小,求最小费用。
限制条件:
1 <= N, M <= 10000
0<= R <= 50000
0 < d < 10000
思路:求最小费用,即相当于建立亲密关系的最大生成树,亦即亲密关系值取反得到最小生成树,本题采用 kruskal 方法。
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 50050;
int N, M, R;
int x[MAX_N], y[MAX_N], d[MAX_N];
class edge{
public:
int from, to, cost;
};
edge G[MAX_N];
int V, E;
int f[MAX_N], rank[MAX_N];
typedef long long LL;
bool cmp(const edge& e1, edge& e2){
return e1.cost<e2.cost;
}
void init_union_find(int v){
for(int i=0; i<v; ++i){
f[i] = i;
rank[i] = 0;
}
}
int find(int x){
return f[x] = f[x]==x ? x : find(f[x]);
}
bool same(int x, int y){
return find(x)==find(y);
}
void unite(int x, int y){
x = find(x);
y = find(y);
if(x!=y){
if(rank[x] < rank[y]) f[x] = y;
else{
f[y] = x;
if(rank[x]==rank[y]) rank[x]++;
}
}
}
int kruskal(){
sort(G, G+V, cmp);
init_union_find(V);
int res = 0;
for(int i=0; i<E; ++i){
edge e = G[i];
if(!same(e.from, e.to)){
unite(e.from, e.to);
res += e.cost;
}
}
return res;
}
void solve(){
V = N + M;
E = R;
for(int i=0; i<R; ++i) G[i] = edge{x[i], y[i], -d[i]};
LL res = 10000*V + kruskal();
cout << res << endl;
}
int main()
{
int t; cin >> t;
while(t--){
cin >> N >> M >> R;
int m;
for(int i=0; i<R; ++i){
cin >> x[i] >> m >> d[i];
y[i] = m+N;
}
solve();
}
return 0;
}