【poj 2784】Buy or Build (最小生成树)

这是一道最小生成树的题,对理解Kruskal算法很有好处。

数据较小,直接考虑枚举购买“子网络”的情况。

采用Kruskal算法,注意每种情况都要重新初始化并查集。

在购买子网络时就将各个子网络unite()(不同的子网络不需要!)。

复杂度O(2^q * N^2 * lgN)(边有n*(n-1)/2条)

(计算发现最大的时间数量级是1e8,用了190ms)

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e3;
const int INF = 0x3f3f3f3f;

int par[N + 1], ran[N + 1], dx[N + 1], dy[N + 1];
vector<int> vec[10];
int qw[10];

struct edge {
    int x, y, cost;
} es[N * N + 1];

bool cmp(edge x1, edge x2) { return x1.cost < x2.cost; };

void init(int x) {
    for (int i = 0; i <= x; i++) {
        par[i] = i;
        ran[i] = 0;
    }
}

int find(int x) {
    if (par[x] == x) return x;
    return par[x] = find(par[x]);
}

void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (ran[x] < ran[y])
        par[x] = y;
    else {
        par[y] = x;
        if (ran[x] == ran[y]) ran[x]++;
    }
}

bool same(int x, int y) { return find(x) == find(y); }

int dis(int x, int y) {
    return (dx[x] - dx[y]) * (dx[x] - dx[y]) +
           (dy[x] - dy[y]) * (dy[x] - dy[y]);
}

int main() {
    int t, n, q, x, y;
    scanf("%d", &t);
    while (t--) {
        //注意读入格式
        getchar();
        scanf("%d%d", &n, &q);
        for (int i = 0; i < q; i++) vector<int>().swap(vec[i]);
        for (int i = 0; i < q; i++) {
            //读入子网络的信息
            scanf("%d%d", &x, &qw[i]);
            for (int j = 0; j < x; j++) {
                scanf("%d", &y);
                vec[i].push_back(y);
            }
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d%d", &dx[i], &dy[i]);
        }
        int l = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                //计算所有点与点之间的边, j=i+1可以省去重复的边
                es[l++] = {i, j, dis(i, j)};
            }
        }
        sort(es, es + l, cmp);
        int res = INF;
        for (int i = 0; i < (1 << q); i++) {
            //穷举所有购买子网络的方案
            init(n);
            int tres = 0;
            //每次都要初始化
            for (int j = 0; j < q; j++) {
                if ((i >> j) & 1) {
                    for (int k = 0; k < vec[j].size() - 1; k++) {
                        unite(vec[j][k], vec[j][k + 1]);
                    }
                    tres += qw[j];
                }
            }
            // Kruskal算法
            for (int j = 0; j < l; j++) {
                if (!same(es[j].x, es[j].y)) {
                    unite(es[j].x, es[j].y);
                    tres += es[j].cost;
                }
            }
            res = min(res, tres);
        }
        //注意输出格式
        printf("%d\n", res);
        if (t) printf("\n");
    }
}

猜你喜欢

转载自www.cnblogs.com/hs-zlq/p/11141897.html
今日推荐