POJ 3723 Conscription 【最大生成树||最大权森林】

时间:2021-09-30 12:40:11

传送门:POJ 3723 Conscription

描述:

Conscription
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 11514   Accepted: 4078

Description

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, NM and R.
Then R lines followed, each contains three integers xiyi and di.
There is a blank line before each test case.

1 ≤ NM ≤ 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

Source

POJ Monthly Contest – 2009.04.05, windy7926778

题意:

要征兵n个男兵和m个女兵,每个花费10000元,但是如果已经征募的男士兵中有和将要征募的女士兵关系好的,那么可以减少花费,给出关系,求最小花费。


思路:

这个题目初始一个是个二分图,以为可以从这里入手,但是这个题目这个性质没用,有时候就是无用陷阱。

初始花费没人10000,那么减去其中有关系的就是当前的花费。

要是花费最少,那么减去的最大即可,又因为没人只征募一次,即最多选择一个,所以减去一个最大生成树就好了(转化为最大权森林问题)

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 2e4+10; //最大点数
int p[maxn]; //并查集的使用

struct edge{
int u,v,cost;
}e[55555];

bool cmp(const edge& a, const edge& b){
return a.cost<b.cost;
}

void init(){/*初始化i的父亲节点为i */
for(int i=0 ; i<maxn ; i++){
p[i]=i;
}
}

int Find(int x){//查找x的父亲节点,递归调用
return p[x]==x?x:p[x]=Find(p[x]);
}

int kruskal(int n,int m){ //返回最小生成树的权值,如果不连通返回-1
int ans=0;
sort(e,e+m,cmp);
for(int i=0 ; i<m ; i++){
int u=e[i].u;
int v=e[i].v;
u=Find(u);
v=Find(v);
if(u!=v){ //如果不在一个集合中,合并
ans+=e[i].cost;
p[v]=u;
}
}
return ans;
}

int main(){
int n,m,r,T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&r);
for(int i=0; i<r; i++){
int x;
scanf("%d%d%d",&e[i].u,&x,&e[i].cost);
e[i].v=x+n; e[i].cost=-e[i].cost;
}
init();
int ans=kruskal(n+m, r);
printf("%d\n",10000*(n+m)+ans);
}
return 0;
}