poj3723:Conscription

题目描述

招募n名女兵和m名男兵,招募每名士兵需要花费10000元,但如果男兵和女兵存在亲密度为d的关系,则当招募其中一名后,招募另一名只需要花费10000-d元,现给出男兵和女兵的关系,求出招募最小花费

分析

最小生成树

要求最小花费,则需要男女兵之间的亲密度越大越好,即求最大生成树,可以将边权变为负数,转化为求最小生成树

注意:女兵编号为0 ~ n-1,男兵编号为0 ~ m-1,因此输入的男兵编号要偏移n,将所有男兵女兵映射到n+m的空间

代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;

const int MAXSIZE = 1E4 + 10;
int father[2*MAXSIZE];      //并查集
int height[2*MAXSIZE];      //并查集中每组高度

void Init(){
    for (int i = 0; i < 2 * MAXSIZE; ++i) {
        father[i] = i;
        height[i] = 0;
    }
}

//查找操作+路径压缩
int Find(int x){
    if(x != father[x]){
        father[x] = Find(father[x]);
    }
    return father[x];
}

//合并操作
void Unite(int x, int y){
    x = Find(x);
    y = Find(y);
    if(x == y){
        return;
    }
    //小树往大树上并,以降低书高
    if(height[x] < height[y]){
        father[x] = y;
    }
    else{
        father[y] = x;
        if (height[x] == height[y]){
            height[x]++;
        }
    }
}

struct Edge{
    int from, to, d;
    Edge(int f, int t, int d): from(f), to(t), d(d) {}
};
vector<Edge> edges;

int cmp(Edge a, Edge b){
    return a.d < b.d;
}

int Kruskal(int r){
    int ans = 0;
    Init();
    for (int i = 0; i < r; ++i) {
        Edge e = edges[i];
        if(Find(e.from) == Find(e.to)){
            continue;
        }
        ans += e.d;
        Unite(e.from, e.to);
    }
    return ans;
}


int main(){
    int k, n, m, r;
    int x, y, d;
    scanf("%d", &k);
    while(k--){
        scanf("%d%d%d", &n, &m, &r);
        edges.clear();
        for (int i = 0; i < r; ++i) {
            scanf("%d%d%d", &x, &y, &d);
            /*注意此处n+y*/
            edges.push_back(Edge(x, n + y, -d));
        }

        sort(edges.begin(), edges.end(), cmp);
        printf("%d\n", 10000 * (n + m) + Kruskal(r));
    }
    return 0;
}
posted @ 2022-02-15 12:57  dctwan  阅读(53)  评论(0编辑  收藏  举报