码迷,mamicode.com
首页 > 其他好文 > 详细

UVa 1151 Buy or Build (最小生成树+二进制法暴力求解)

时间:2016-06-21 22:40:22      阅读:184      评论:0      收藏:0      [点我收藏+]

标签:

题意:给定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\n", ans);
        if(T)  printf("\n");
    }
    return 0;
}

 

UVa 1151 Buy or Build (最小生成树+二进制法暴力求解)

标签:

原文地址:http://www.cnblogs.com/dwtfukgv/p/5605004.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!