Conscription
Windy has a country, and he wants to build an army to protect his country. He has picked up N girls and M boys and wants to collect them to be his soldiers. To collect a soldier without any privilege, he must pay 10000 RMB. There are some relationships between girls and boys and Windy can use these relationships to reduce his cost. If girl x and boy y have a relationship d and one of them has been collected, Windy can collect the other one with 10000-d RMB. Now given all the relationships between girls and boys, your assignment is to find the least amount of money Windy has to pay. Notice that only one relationship can be used when collecting one soldier.
Input
The first line of input is the number of test case.
The first line of each test case contains three integers, N, M and R.
Then R lines followed, each contains three integers xi, yi and di.
There is a blank line before each test case.
1 ≤ N, M ≤ 10000
0 ≤ R ≤ 50,000
0 ≤ xi < N
0 ≤ yi < M
0 < di < 10000
Output
For each test case output the answer in a single line.
Sample Input
2
5 5 8
4 3 6831
1 3 4583
0 0 6592
0 1 3063
3 3 4975
1 3 2049
4 2 2104
2 2 781
5 5 10
2 4 9820
3 2 6236
3 1 8864
2 4 8326
2 0 5156
2 0 1463
4 1 2439
0 4 4373
3 4 8889
2 4 3133
Sample Output
71071
54223
C++编写:
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
const int MAX_R=50010;
struct edge{
int u,v,cost;
};
int num,N,M,R; //招募N个女兵,M个男兵
int V,E; //V个节点,E条边
int par[MAX_R]; //父亲
int depth[MAX_R]; //树的深度
int x[MAX_R],y[MAX_R],d[MAX_R]; //用于记录x、y、d
edge es[MAX_R]; //记录两个节点之间的关系
void init(int n) //初始化n个元素
{
for(int i=0;i<n;i++)
{
par[i]=i;
depth[i]=0;
}
}
int find(int x) //查询树的根
{
if(par[x]==x) return x;
else return par[x]=find(par[x]);
}
void unite(int x,int y) //合并x和y所属的集合
{
x=find(x);
y=find(y);
if(depth[x]<depth[y])
par[x]=y;
else
{
par[y]=x;
if(depth[x]==depth[y]) depth[x]++;
}
}
bool same(int x,int y) //判断x和y是否属于同一个集合
{
return find(x)==find(y);
}
bool compare(const edge& e1,const edge& e2) //设定排列顺序
{
return e1.cost<e2.cost;
}
int kruskal()
{
/*按照cost也就是亲密度从小到大的顺序排列
*那么这个地方由于每个cost的值都为负值
*从绝对值来看就是从大到小排列了
*最终减去的值的绝对值就是最大值
*/
sort(es,es+E,compare);
init(V); //初始化并查集,即初始化V个节点
int res=0; //记录最小权值
for(int i=0;i<E;i++)
{
edge e=es[i];
if(!same(e.u,e.v))
{
unite(e.u,e.v);
res+=e.cost;
}
}
return res;
}
void solve()
{
cin>>N>>M>>R;
for(int i=0;i<R;i++)
scanf("%d%d%d",&x[i],&y[i],&d[i]);
V=N+M;
E=R;
for(int i=0;i<R;i++)
es[i]=(edge){x[i],N+y[i],-d[i]}; //把所有边权全部取反
cout<<10000*(N+M)+kruskal()<<endl; //用总费用-亲密度最大值
}
int main()
{
cin>>num;
while(num--)
solve();
}