UVa #1151 Buy or Build (例题11-3)

本文介绍了一种在特定约束条件下优化最小生成树计算的方法。通过预先计算不包含特殊套餐节点的最小生成树,并利用并查集高效更新树结构,避免了暴力枚举所有可能带来的高时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

套餐只有8个,可以暴力枚举。不过边太多,不能枚举套餐都求一遍最小生成树。


其实只需要在一开始求出不包含套餐的最小生成树,把这些边记下来备用。之后枚举套餐的时候,先把套餐中的点全部加入并查集,然后在原最小生成树的基础上,再来求最小生成树就可以了。


那些一开始就没有被加入最小生成树的边,到了暴力枚举又加入很多权值为0的边排在他们前面,就更加不会被包含到最小生成树中了。



Run Time: 0.245s

#define UVa  "LT11-3.1151.cpp"      //Buy or Build

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>

using namespace std;

//Global Variables.
const int maxn = 1000 + 5, maxe = maxn*maxn, maxq = 10;
int T, n, q, m;
int x[maxn], y[maxn];
int u[maxe], v[maxe], w[maxe], r[maxe];
int qsize[maxq], qcost[maxq], qcities[maxq][maxn];
vector<int> min_span_tree;
int p[maxn];
int ans;
////

int cmp(int a, int b) { return w[a] < w[b]; }
int calc_w(int a, int b) { return (x[a]-x[b])*(x[a]-x[b]) + (y[a]-y[b])*(y[a]-y[b]); }

int find(int s) {               //union-find set
    int t = s;
    while(s != p[s]) s = p[s];
    while(p[t] != s) {          //change p along the path
        int tmp = p[t];
        p[t] = s;
        t = tmp;
    }
    return s;
}

void kruscal() {
    min_span_tree.clear();
    ans = 0;
    for(int i = 0; i < m; i ++) {
        int e = r[i];
        int x = find(u[e]), y = find(v[e]);
        if(x != y) {        //include in min-span tree.
            p[x] = y;
            ans += w[e];
            min_span_tree.push_back(e);
        }
    }
}
void print_bin(int a) {
    for(int i = 0; i < q; i ++) {
        printf("%d", a%2);
        a/=2;
    }
    printf("\n");
}

int solve() {
    for(int s = 1; s < (1<<q); s ++) {      //iterating all existing networks.
        int tmp = 0;
        for(int i = 1; i <= n; i ++) p[i] = i;      //reset union-find set.
        for(int k = 0; k < q; k ++) if((1<<k) & s){
            tmp += qcost[k];
            for(int i = 0; i < qsize[k]; i ++){
                for(int j = i+1; j < qsize[k]; j ++){
                    int x = find(qcities[k][i]), y = find(qcities[k][j]);
                    if(x != y) p[x] = y;
                }
            }
        }
        for(int i = 0; i < min_span_tree.size(); i ++) {
            int e = min_span_tree[i];
            int x = find(u[e]), y = find(v[e]);
            if(x != y) {
                p[x] = y;
                tmp += w[e];
            }
        }
        ans = min(ans, tmp);
    }
    return 0;
}

int main(){
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &q);
        for(int i = 0; i < q; i ++) {
            scanf("%d%d", &qsize[i], &qcost[i]);
            for(int j = 0; j < qsize[i]; j ++)
                scanf("%d", &qcities[i][j]);
        }
        for(int i = 1; i <= n; i ++) {
            scanf("%d%d", &x[i], &y[i]);
            p[i] = i;
        }
        m = 0;
        for(int i = 1; i <= n; i ++) {
            for(int j = i+1; j <= n; j ++) {
                r[m] = m;
                u[m] = i;
                v[m] = j;
                w[m] = calc_w(i, j);
                m ++;
            }
        }
        sort(r, r+m, cmp);

        kruscal();
        solve();
        printf("%d\n", ans);
        if(T) printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值