1. 程式人生 > >POJ 3723-Constription [最大權森林] 《挑戰程式設計競賽》2.5

POJ 3723-Constription [最大權森林] 《挑戰程式設計競賽》2.5

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美元. 帶式如果已經徵募的人中有一些關係親密的人, 那麼可以少花一些錢. 給出若干的男女之前的1~9999指尖的親密關係,徵募某個人的費用是10000-(已經招募的人中和自己的親密度最的最大值). 要求通過適當的徵募順序使得徵募所有人所用的費用最小.

題解:

在徵募某個人a時,如果使用來a和b之間的關係,那麼就連一條a到b的邊.假設這個圖中存在圈,那麼無論以什麼順序徵募這個圈上的所有人, 都會產生矛盾.(只有男女關係是產生不了圈的…)因此可以直到這個圖是一片森林. 反之,如果給了一片森林那麼就可以使用對應的關係確定徵募的順序.
可以把人看作頂點, 關係看作邊,這個問題就可以轉化為求解無向圖中的最大權森林問題.最大權森靈問題可以通過把所有邊取反之後用最小生成樹的演算法求解.

程式碼:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
#define MAX_R 60000
#define MplusN 30000
using namespace std;

struct Edge {
    int u, v, w;
};
int N, M, R;
int f[MplusN];
vector<Edge> edges(MAX_R);

bool cmp(Edge &a, Edge &b) {
    return a.w < b.w;
}
void f_init(int n) {
    for (int i = 0; i <= n; i++) 
        f[i] = i;
}
int find(int x) {
    if (f[x] == x) return x;
    else return f[x] = find(f[x]);
}
void join(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) {
        f[fx] = fy;
    }
}
int kruskal() {
    int sum = 0;
    f_init(N+M);
    sort(edges.begin(), edges.begin() + R, cmp);
    vector<Edge>::iterator i;
    for (i = edges.begin(); i < edges.begin() + R; i++) {
        int u = i->u, v = i->v;
        int fu = find(u), fv = find(v);
        if (fu != fv) {
            join(fu, fv);
            sum += i->w;
        }
    }
    return sum;
}

int main() {
    int n;
    int x, y, d;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d%d%d", &N, &M, &R);
        for (int i = 0; i < R; i++) {
            scanf("%d%d%d", &x, &y, &d);
            edges[i].u = x; 
            edges[i].v = y + N; 
            edges[i].w = -d;
        }
        cout << 10000 * (N + M) + kruskal() << endl;
    }
    return 0;
}