题目链接:[POJ3723]Conscription[并查集][kruskal]
第一次做并查集的题目,其实之前我连什么是并查集豆不知道TAT
今儿一天就混这两个概念了,mark下。代码中有些注释,诸君请便。
关于并查集的理解,推荐一篇博文:快点我~~
这题kruskal找到的是所有人都纳入进来,能得到的最大回扣值。
具体代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int MAXN = 20200;
int n, m, r;
struct Edge{
int u, v, cost;
bool operator < (const Edge& rhs) const{
return cost < rhs.cost;
}
}G[50005];
int par[MAXN], ran[MAXN];
void init(int n){
for (int i = 0; i < n; ++i)
{
par[i] = i; //parant数组。记录点的父亲节点
ran[i] = 0; //ran数组。记录节点高度
}
return;
}
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 (ran[x] < ran[y]) par[x] = y;
else {
par[y] = x;
if (ran[x] == ran[y]) ++ran[x];
}
return;
}
bool same(int x, int y){ //是否拥有同一个父节点
return find(x) == find(y);
}
int kruskal(){
int ret = 0;
sort(G + 1, G + 1 + r);
init(n + m);
for (int i = 1; i <= r; ++i){
Edge &e = G[i];
if (!same(e.u, e.v)){
ret += e.cost;
unite(e.u, e.v);
}
} //小mark:当两个子节点被联结后,如果又有一个同样的关系,这样联结的结果就是不记入钱数,满足发生两人多次关系取其最大减少费用这一情况。(毕竟排过序嘛)
return ret;
}
int main() {
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--){
scanf("%d%d%d",&n, &m, &r);
for (int i = 1; i <= r; ++i){
int x, y, v; scanf("%d%d%d", &x, &y, &v);
G[i] = (Edge){x, n + y, -v};
}
int ans = 10000 * (n + m) + kruskal();
printf("%d\n",ans);
}
return 0;
}