看了挑战上的这道题,一开始根本没反应过来这就是最大生成树。不过仔细想想,如果我们把是朋友或者间接是朋友的人都放在一个集合,那么但我们通过a来招募b的时候,就在a和b中间连一条边,权值就是a和b的亲密度。当我们把这个集合中的人都招募完的时候,是不是集合中的人都被一条条边连接起来了呢?此外,为了使我们的总花费最小,我们肯定希望他们的亲密度之和最大(节省的钱最多),也就是使得这个连通块的边权值最大。这不就是最大生成树吗?(肯定有一个人是花费1w来招募的,然后通过他来招募这个集合的其他人)
如果还想不明白的话,换个角度。肯定有一个人是要花费1w来招募的,我们先招募他,然后在和他认识的人中间找到一个亲密度最大的来招募,然后依次类推,每次都往集合里面插入一个人。这是不是类似prim算法呢?所以从这个角度我们也推出了这是最大生成树问题。
还有一点要注意,因为男女士兵的标号都是0—n - 1.但是实际上他们是不一样的,如果输入的数据是0,0,5000.这就会出现错误。所以我们需要将他们都给区分开。起初我用的是map,一一映射,最后超时了。看了网上大神的代码才发现自己是这么蠢。只需要将女兵的编号加上N(男兵的总数)就行了。
代码如下;
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <complex>
#include <cstring>
#include <utility>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <new>
#include <set>
#include <map>
#define lson l, m, k << 1
#define rson m, r, k << 1 | 1
using namespace std;
typedef long long int LL;
const int INF = 0x3f3f3f3f;
const int maxn = 100005;
int p[maxn];
int N, M, R;
int find_root(int x){//并查集查找根节点
if (p[x] == x) return x;
return p[x] = find_root(p[x]);
}
struct edge{
int u, v, dis;
edge(int a, int b, int c){u = a; v = b; dis = c;}
edge(){}
bool operator < (const edge &b) const{
return dis < b.dis;
}
};
edge ed[maxn];
int kruskal(){
for (int i = 0; i < maxn; i++)//并查集初始化
p[i] = i;
sort(ed, ed + R);
int res = 0;
for (int i = 0; i < R; i++){
edge e = ed[i];
int rx = find_root(e.u);
int ry = find_root(e.v);
if (rx != ry){
p[rx] = ry;
res += e.dis;
}
}
return res;
}
int main()
{
//freopen("1.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--){
scanf("%d%d%d", &N, &M, &R);
int x, y, d, cnt = 0;
for (int i = 0; i < R; i++){
scanf("%d%d%d", &x, &y, &d);
ed[i] = edge(x, y + N, -d);
}
printf("%d\n", 10000 * (M + N) + kruskal());
}
return 0;
}