zoukankan      html  css  js  c++  java
  • UVA 1151 Buy or Build (买还是建)(并查集+二进制枚举子集)

    题意:平面上有n个点(1<=n<=1000),你的任务是让所有n个点连通。可以新建边,费用等于两端点欧几里德距离的平方。也可以购买套餐(套餐中的点全部连通)。问最小费用。

    分析:

    1、先将不购买任何套餐的最小生成树的所有边(边数为cnt)存起来,目的是枚举套餐时不必再耗Kruskal算法的O(n2)复杂度,而是降低为O(cnt)。

    2、二进制枚举套餐。

    3、枚举套餐时,先将套餐中的边按最小生成树建边,在将不购买任何套餐的最小生成树的cnt条边建上,因为套餐中的边权值为0,所以这样处理不会影响结果。

    #pragma comment(linker, "/STACK:102400000, 102400000")
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<cmath>
    #include<iostream>
    #include<sstream>
    #include<iterator>
    #include<algorithm>
    #include<string>
    #include<vector>
    #include<set>
    #include<map>
    #include<stack>
    #include<deque>
    #include<queue>
    #include<list>
    #define Min(a, b) ((a < b) ? a : b)
    #define Max(a, b) ((a < b) ? b : a)
    const double eps = 1e-8;
    inline int dcmp(double a, double b){
        if(fabs(a - b) < eps) return 0;
        return a > b ? 1 : -1;
    }
    typedef long long LL;
    typedef unsigned long long ULL;
    const int INT_INF = 0x3f3f3f3f;
    const int INT_M_INF = 0x7f7f7f7f;
    const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
    const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;
    const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
    const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
    const int MOD = 1e9 + 7;
    const double pi = acos(-1.0);
    const int MAXN = 1000 + 10;
    const int MAXT = 10000 + 10;
    using namespace std;
    vector<int> v;//不选择套餐时最小生成树的边编号
    int ans, n, m;
    int fa[MAXN];
    struct City{//城市
        int x, y, id;
        void read(){
            scanf("%d%d", &x, &y);
        }
    }num[MAXN];
    struct Package{//套餐
        int n, cost;
        int city[MAXN];
        void read(){
            scanf("%d%d", &n, &cost);
            for(int i = 0; i < n; ++i){
                scanf("%d", &city[i]);
            }
        }
    }q[10];
    struct Edge{
        int from, to, dist;
        void set(int f, int t, int d){
            from = f;
            to = t;
            dist = d;
        }
        bool operator < (const Edge& rhs)const{
            return dist < rhs.dist;
        }
    }e[MAXN * MAXN];
    int getD(City& A, City& B){
        return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y);
    }
    int Find(int v){
        return fa[v] = (fa[v] == v) ? v : Find(fa[v]);
    }
    void solve(){//二进制枚举子集
        for(int i = 1; i < (1 << m); ++i){
            int tmp = 0;
            for(int j = 1; j <= n; ++j) fa[j] = j;//初始化并查集
            for(int j = 0; j < m; ++j){
                if(i & (1 << j)){
                    tmp += q[j].cost;
                    for(int a = 0; a < q[j].n; ++a){
                        for(int b = a + 1; b < q[j].n; ++b){
                            int x = Find(q[j].city[a]);
                            int y = Find(q[j].city[b]);
                            if(x == y) continue;
                            if(x < y) fa[y] = x;
                            else fa[x] = y;
                        }
                    }
                }
            }
            int len = v.size();
            for(int j = 0; j < len; ++j){
                int id = v[j];
                int x = Find(e[id].from);
                int y = Find(e[id].to);
                if(x == y) continue;
                tmp += e[id].dist;
                if(x < y) fa[y] = x;
                else fa[x] = y;
            }
            ans = Min(ans, tmp);
        }
    }
    int main(){
        int T;
        scanf("%d", &T);
        while(T--){
            scanf("%d%d", &n, &m);
            for(int i = 0; i < m; ++i){
                q[i].read();
            }
            for(int i = 1; i <= n; ++i){
                num[i].read();
                fa[i] = i;
            }
            int cnt = 0;
            for(int i = 1; i <= n; ++i){
                for(int j = i + 1; j <= n; ++j){
                    e[cnt++].set(i, j, getD(num[i], num[j]));
                }
            }
            sort(e, e + cnt);
            v.clear();
            ans = 0;
            for(int i = 0; i < cnt; ++i){
                int x = Find(e[i].from);
                int y = Find(e[i].to);
                if(x == y) continue;
                ans += e[i].dist;
                v.push_back(i);
                if(x < y) fa[y] = x;
                else fa[x] = y;
            }
            solve();
            printf("%d\n", ans);
            if(T) printf("\n");
        }
        return 0;
    }
    

      

  • 相关阅读:
    redis使用基础(一) ——Redis基本概述与安装配置
    Linux学习闲谈(三) ——SVN用法及切版本与合版本
    Linux学习闲谈(二) ——SVN版本控制拾遗
    Linux学习闲谈(一)——Shell基本操作与命令
    linux1
    git命令
    ubuntu
    laradock
    实用工具
    grumphp在docker里问题
  • 原文地址:https://www.cnblogs.com/tyty-Somnuspoppy/p/6394166.html
Copyright © 2011-2022 走看看