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;
    }
    
  • 相关阅读:
    HDU 3681 Prison Break 越狱(状压DP,变形)
    POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)
    ZOJ 3471 Most Powerful (状压DP,经典)
    POJ 2288 Islands and Bridges (状压DP,变形)
    HDU 3001 Travelling (状压DP,3进制)
    POJ 3311 Hie with the Pie (状压DP)
    POJ 1185 炮兵阵地 (状压DP,轮廓线DP)
    FZU 2204 7
    POJ 3254 Corn Fields (状压DP,轮廓线DP)
    ZOJ 3494 BCD Code (数位DP,AC自动机)
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/5605004.html
Copyright © 2011-2022 走看看