http://poj.org/problem?id=3723
之前看过但是不理解,今天看了一下感觉是恍然大悟的感觉。
对于每个人之间的关系可以看做是一条边,亲密度可以看做是这条边的权值。既然我们要招募所有人(联想到最小生成树)使这个费用最小,那么亲密度应该尽量选择大的,这里可以巧妙的处理,将权值都取负,正数的最大不就是负数的最小吗?
但这里要注意到这不一定是一棵树,可能是森林,题目中没有保证说所有人之间都是连通的。
不过我们还是可以使用最小生成树来做。找出可以经过所有人的最小的边。
WA了好几发,因为合并集合的时候写错了,应该将这条边一个点的祖先添加到另一个点的祖先下面,而不是将一个点添加到另一个点下面。。。。
太久没写都生疏了。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define M 100009 typedef struct edge { int from; int to; int cost; }edge; int n,m,r; int p[M]; edge e[M]; int ans; int cmp(edge a,edge b) { return a.cost < b.cost; } int find(int x) { return x==p[x]? x: p[x]=find(p[x]); } int kruskal() { for(int i = 0;i < n+m;i++) p[i] = i; for(int i = 0;i < r;i++) { /*int x = e[i].from; int y = e[i].to; if(find(x)!=find(y)) { p[x] = y; //找了好久的错。。。要写成p[find(x)] = y; ans += e[i].cost; }*/ int x = find(e[i].from); int y = find(e[i].to); if(x!=y) { p[x] = y; //一定要记得集合合并的时候是将一个的祖先已到另一个祖先的下面,从而达到两个集合的合并 ans += e[i].cost; } } return ans; } int main() { int t; scanf("%d",&t); while(t--) { //memset(e,0,sizeof(e)); ans = 0; scanf("%d %d %d",&n,&m,&r); for(int i = 0;i < r;i++) { int a,b,c; scanf("%d %d %d",&a,&b,&c); e[i] = (edge){a,b+n,-c}; } sort(e,e+r,cmp); printf("%d\n",10000*(n+m)+kruskal()); } return 0; }