poj3723:Conscription
题目描述
招募n名女兵和m名男兵,招募每名士兵需要花费10000元,但如果男兵和女兵存在亲密度为d的关系,则当招募其中一名后,招募另一名只需要花费10000-d元,现给出男兵和女兵的关系,求出招募最小花费
分析
最小生成树
要求最小花费,则需要男女兵之间的亲密度越大越好,即求最大生成树,可以将边权变为负数,转化为求最小生成树
注意:女兵编号为0 ~ n-1,男兵编号为0 ~ m-1,因此输入的男兵编号要偏移n,将所有男兵女兵映射到n+m的空间
代码
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXSIZE = 1E4 + 10;
int father[2*MAXSIZE]; //并查集
int height[2*MAXSIZE]; //并查集中每组高度
void Init(){
for (int i = 0; i < 2 * MAXSIZE; ++i) {
father[i] = i;
height[i] = 0;
}
}
//查找操作+路径压缩
int Find(int x){
if(x != father[x]){
father[x] = Find(father[x]);
}
return father[x];
}
//合并操作
void Unite(int x, int y){
x = Find(x);
y = Find(y);
if(x == y){
return;
}
//小树往大树上并,以降低书高
if(height[x] < height[y]){
father[x] = y;
}
else{
father[y] = x;
if (height[x] == height[y]){
height[x]++;
}
}
}
struct Edge{
int from, to, d;
Edge(int f, int t, int d): from(f), to(t), d(d) {}
};
vector<Edge> edges;
int cmp(Edge a, Edge b){
return a.d < b.d;
}
int Kruskal(int r){
int ans = 0;
Init();
for (int i = 0; i < r; ++i) {
Edge e = edges[i];
if(Find(e.from) == Find(e.to)){
continue;
}
ans += e.d;
Unite(e.from, e.to);
}
return ans;
}
int main(){
int k, n, m, r;
int x, y, d;
scanf("%d", &k);
while(k--){
scanf("%d%d%d", &n, &m, &r);
edges.clear();
for (int i = 0; i < r; ++i) {
scanf("%d%d%d", &x, &y, &d);
/*注意此处n+y*/
edges.push_back(Edge(x, n + y, -d));
}
sort(edges.begin(), edges.end(), cmp);
printf("%d\n", 10000 * (n + m) + Kruskal(r));
}
return 0;
}