zoukankan      html  css  js  c++  java
  • 题解 UVA1151Buy or Build (最小生成树)

    题意

    平面上有n((nleq1000))个点,你的任务是让所有n个点联通。为此,你可以新建一些边,费用等于两个端点的欧几里得距离平方。另外还有q((qleq8))个套餐可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连接。第i个套餐的花费为(C_i)

    算法

    最小生成树

    分析

    朴素算法:枚举要选择哪些套餐,跑最小生成树;

    复杂度:枚举:O((2^q)) 排序O((n^2)(logn)) 总O((2^q)(n^2) (+) (n^2)(logn)); 显然是接受不了的

    优化:先求一遍Kruskal,得到了(n-1)条边,然后枚举套餐,跑最小生成树时只考虑这(n-1)条边

    简单证明:买了套餐后,相当于一些边的边权变成了0,而对于不在套餐中的每条边e,排序在e之前的边一个都没少,反而还多了一些权值为0的边,所以在原图Kruskal时被“扔掉”的边,在后面的枚举套餐中也一样会被扔掉。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 1010, maxm = 1e6 + 10;
    int n,fa[maxn],ans,sum,q,x[maxn],y[maxn],w[maxn][maxn],num,c[maxn],_cnt;
    struct Edge{
        int from,to,val;
    }e[maxm];
    Edge r[maxn]; //要用的边
    
    bool cmp(Edge x, Edge y){return x.val < y.val;}
    
    int get(int x){return fa[x] == x ? x : fa[x] = get(fa[x]);}
    
    void Kruskal0(){
        int cnt = 0;
        sort(e + 1, e + 1 + num, cmp);
        for(int i = 1; i <= num; ++ i){
            int u = e[i].from, v = e[i].to;
            int q1 = get(u), q2 = get(v);
            if(q1 != q2){
                fa[q1] = q2; cnt ++;
                r[cnt] = e[i]; ans += e[i].val;  //保留要用的边
            }
            if(cnt >= n - 1) break;
        }
        num = cnt;
    }
    
    void Kruskal(){
        for(int i = 1; i <= num; ++ i){
            int q1 = get(r[i].from), q2 = get(r[i].to);
            if(q1 != q2){
                fa[q1] = q2; _cnt ++;
                sum += r[i].val;
            }
            if(_cnt >= n - 1) break;
        }
    }
    
    void get_ans(){
        for(int ss = 0; ss < (1 << q); ++ ss){  //状压枚举
            sum = 0; _cnt = 0;
            for(int i = 1; i <= n; ++ i) fa[i] = i;
            for(int k = 1; k <= q; ++ k)
                if(ss & (1 << k - 1)){
                    sum += c[k];
                    for(int i = 1; i < w[k][0]; ++ i)
                        for(int j = i + 1; j <= w[k][0]; ++ j){
                            int q1 = get(w[k][i]), q2 = get(w[k][j]);
                            if(q1 != q2) fa[q1] = q2, _cnt ++;
                        }
                }
            Kruskal();
            ans = min(ans, sum);
        }
    }
    
    int main(){
        int T;
        scanf("%d", &T);
        while(T--){
            sum = ans = num = 0;
            scanf("%d%d", &n, &q);
            for(int i = 1; i <= n; ++ i) fa[i] = i;
            for(int i = 1; i <= q; ++ i){
                scanf("%d%d", &w[i][0], &c[i]);
                for(int j = 1; j <= w[i][0]; ++ j) scanf("%d", &w[i][j]);
            }
            for(int i = 1; i <= n; ++ i) scanf("%d%d", &x[i], &y[i]);
            for(int i = 1; i < n; ++ i)
                for(int j = i + 1; j <= n; ++ j){
                    int dis = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
                    e[++num].from = i, e[num].to = j, e[num].val = dis;
                }
            Kruskal0();
            get_ans();
            printf("%d
    ", ans);
            if(T) cout << endl;   //UVA的神仙格式
        }
        return 0;
    }
    
  • 相关阅读:
    基于mysql-JDBC的java编程(通过java连接数据库)
    mysql-windows系统上的安装配置
    Jsp编程的基础模型
    CSPRNG随机数产生器
    IntlChar()
    unserialize()过滤机制
    Closure::call()
    PHP匿名类
    LINUX 安装PHP7教程
    解析UNICODE方法
  • 原文地址:https://www.cnblogs.com/whenc/p/13784993.html
Copyright © 2011-2022 走看看