zoukankan      html  css  js  c++  java
  • UVa 1151 Buy or Build (最小生成树+二进制法暴力求解)

    题意:给定n个点,你的任务是让它们都连通。你可以新建一些边,费用等于两点距离的平方(当然越小越好),另外还有几种“套餐”,可以购买,你购买的话,那么有些边就可以连接起来,

    每个“套餐”,也是要花费的,让你求出最少花费。

    析:首先想到的是把所有情况都考虑算一下,然后找出最少的,先算没有“套餐”的,然后算有的,用二进制枚举的话,总时间复杂度为O(2qn2+n2logn),这个时间复杂度太大了吧,肯定会超时,

    那么我们就可以优化一下,首先先算出来最小生成树,并且把每条边都保存下来,那么加了“套餐”之后,就不用全部枚举了,这是一个优化,然后在买“套餐”后,那么有的权值就变成了0,

    这个也不要加上,再重新枚举,我们在之前就把它们连接上就OK。其他的和最小生成树一样。

    代码如下:

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int maxn = 1000 + 10;
    struct node{
        int u, v, w;
        bool operator < (const node &p) const {
            return w < p.w;
        }
    };
    node a[maxn*maxn/2];
    int p[maxn], x[maxn], y[maxn], n, m, q[8][maxn], c[2][10], indx, l[maxn];
    
    int Find(int x){
        return x == p[x] ? x : p[x] = Find(p[x]);
    }
    
    int Kruskal(){//最小生成树
        int ans = 0;
        l[0] = 0;
        for(int i = 0; i < indx; ++i){
            int x = Find(a[i].u);
            int y = Find(a[i].v);
            if(x != y){
                ans += a[i].w;
                p[x] = y;
                l[++l[0]] = i;//把最小生成树的边都保存下来
            }
        }
        return ans;
    }
    
    int Kruskal2(){//买“套餐”后的最小生成树
        int ans = 0;
        for(int i = 1; i <= l[0]; ++i){
            int ii = l[i];
            int x = Find(a[ii].u);
            int y = Find(a[ii].v);
            if(x != y){
                p[x] = y;
                ans += a[ii].w;
            }
        }
        return ans;
    }
    
    int main(){
    //    freopen("int.txt", "r", stdin);
        int T;  cin >> T;
        while(T--){
            scanf("%d %d", &n, &m);
            for(int i = 0; i < m; ++i){
                scanf("%d %d", &c[0][i], &c[1][i]);
                for(int j = 0; j < c[0][i]; ++j)
                    scanf("%d", &q[i][j]);
            }
            for(int i = 0; i < n; ++i)
                scanf("%d %d", &x[i], &y[i]);
    
            indx = 0;
            for(int i = 0; i < n; ++i)//计算权植
                for(int j = i+1; j < n; ++j){
                    a[indx].u = i+1;
                    a[indx].v = j+1;
                    a[indx++].w = (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]);
                }
    
    
            sort(a, a+indx);
            for(int i = 0; i <= n; ++i)    p[i] = i;
            int ans = Kruskal();//计算不买”套餐“时的最小费用
    
            for(int i = 0; i < (1<<m); ++i){//二进制法枚举
                int cost = 0;
                for(int j = 0; j <= n; ++j)   p[j] = j;
    
                for(int j = 0; j < m; ++j){
                    if(i&(1<<j)){
                        cost += c[1][j];
                        for(int k = 0; k < c[0][j]-1; ++k){
                            int xx = Find(q[j][k]);
                            int yy = Find(q[j][k+1]);
                            if(xx != yy)   p[xx] = yy;
                        }
                    }
                }
                ans = min(ans, cost + Kruskal2());//加上买套餐的费用,更新最小值
            }
    
            printf("%d
    ", ans);
            if(T)  printf("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    2015 ACM/ICPC Asia Regional Changchun Online HDU 5444 Elven Postman【二叉排序树的建树和遍历查找】
    JDBC连接MySQL数据库
    MySQL实习训练1
    I Hate It
    Just a Hook
    Mayor's posters
    asd的甩锅计划
    程序员的恋情
    Radical and array
    Highways
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/5605004.html
Copyright © 2011-2022 走看看