题目链接:[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; }