POJ 3723Conscription

来源:互联网 发布:c语言 eof 编辑:程序博客网 时间:2024/05/16 13:38
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, 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

题意:

就是要找n个女兵,m个男兵,每招一个兵的价钱是10000;然后给出男女兵之间的亲密度。亲密度的作用就是用来省钱,就是要找一个兵时,在已招的兵中,有存在与这个兵有亲密度的,这个兵的价格就为10000-max(亲密度)(就是与已招的兵中最大亲密度);

思路:

这边其实最终是要求最大权值森林,然后这边讲一下为什么可以转化成最大权值森林,以及怎么求解最大权值森林。

1.为什么可以转化成最大权值森林?

首先什么是最大权值森林?这是相对于图来说的,而不是相对于树;首先就得把女兵男兵都看成是一个个的节点,之间的亲密度视为两点之间的权值。这样就构成了图。但是这边的图没有说明一定是连同通的,所以现在就不是一棵树了,而是存在连通分量的森林。到这边其实最后问题的答案就转化称为

ans=(n+m)*10000-最大权和;简要的说就是在这边森林里选边的时候是不能构成回路的,因为若1与2,1与3,2与3之间存在亲密度;你只能计算两个,否则就矛盾了,因为这边是计算的是与已招的兵的亲密度。

2.怎么求解最大权值森林

本来是这个样子的,也就是说最大森林的权值就是把图中所有的权值加起来就是,但是通过上面的分析就是说图中不能存在回路,也就是在一个有n个点的连通分量中,最多只能选n-1条,这边就跟最小生成树及其的像了,区别在于森林有多个连通分量,另外是这边求的是最大值不是最小值;但这边可以转化一下,就是说把亲密度转化为负数,就是求最小权值森林了,ans转化为ans=(n+m)*10000+最小权和(为负数);现在与最小生成树的区别仅在于要求最小生成树的图是连通的,而这边是存在连通分量的,但是用kruskal直接按求值最小生成树的方法来求是没影响的。就是说kruakal算法求解最小权森林也是通用的。


AC代码:


# include <cstdio># include <algorithm> using namespace std;typedef long long int LL;struct edge{//存边 int u;int v;int d;};int father[20010];//存每一个点对应的父亲节点 edge s[50010];//结构体数组 int compare(edge a, edge b){//比较函数 return a.d<b.d;}void init(int n, int m){//初始化并查集 for(int i=0; i<=n+m; i++){father[i]=i;}}int find_root(int n){//查找父亲节点函数 if(father[n]==n){return n;}return father[n]=find_root(father[n]);}int main(){int n, m, r, t, i, j, k, x, y, d, f1, f2;LL sum;edge e;scanf("%d", &t);for(i=1; i<=t; i++){scanf("%d%d%d", &n, &m, &r);for(j=1; j<=r; j++){//输入数据 scanf("%d%d%d", &x, &y, &d);y=y+n;//对y进行转化//女性编号0~n-1, 男性编号n~m-1 s[j].d=-d;s[j].u=x;s[j].v=y;//将d转化为负值 }sum=0;//初始化 sort(s+1, s+1+r, compare);//排序 init(n, m);//初始化并查集 for(j=1; j<=r; j++){//kruskal算法 x=s[j].v;y=s[j].u;d=s[j].d;f1=find_root(x);f2=find_root(y);//查找父亲节点 if(f1!=f2){//不同则合并父亲节点 sum=sum+d;father[f1]=f2;}}printf("%lld\n", (n+m)*(LL)10000+sum);}return 0;}



0 0
原创粉丝点击