【UVA】1151 - Buy or Build(二进制枚举子集 + 并查集)

这题LRJ书上翻译的有问题,书上说两点之间的cost是两点的欧几里得距离,而题目要求两点的距离是两点欧几里得距离的平方。

其余就没什么好说的了,裸的并查集,需要注意的就是二进制枚举子集的问题。

二进制枚举子集:

    for(int i = 0 ; i < (1 << s) ; i++){   /*s是集合元素的个数*/
        for(int j = 0 ; j < s ; j++){
            if(!(s >> j) & 1) 
                continue;
            else{
                
            }

        }
    }

14054883 1151 Buy or Build Accepted C++ 0.118 2014-08-17 10:37:07

注释都加了,就不多解释了:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<list>
#include<cmath>
#include<string>
#include<sstream>
#include<ctime>
using namespace std;
#define _PI acos(-1.0)
#define esp 1e-9
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pill;
/*===========================================
===============KinderRiven===================
===========================================*/
#define MAXD 1100  /*最多1000个点*/
vector<int>array;
int n , m , line_size;
int fa[MAXD];
struct Line{
    int l;
    int r;
    int cost;
    friend bool operator < (Line p,Line q){
        if(p.cost < q.cost)
            return true;
        else
            return false;
    }
}line[MAXD * MAXD];
struct Point{
    int x;
    int y;
}p[MAXD];
struct Pow{
    int cost;
    int size;
    int arr[MAXD];
}q[15];
int _dist(Point p,Point q){
    int x = p.x - q.x;
    int y = p.y - q.y;
    int ans =  x * x +  y * y;
    return ans;
}
int find_father(int u){
    return fa[u] == u ? u : fa[u] = find_father(fa[u]);
}
void init(){
    for(int i = 0 ; i <= n ; i++) fa[i] = i;
    return ;
}
LL solve(int u){
    int now = 0;  /*加的边数*/
    int cost = 0;
    init();
    for(int i = 0 ; i < m ; i ++){
        if(!((u >> i) & 1)) continue;   /*二级制枚举子集*/
        cost += q[i].cost;              /*套餐的花费*/
        for(int j = 1 ; j < q[i].size ; j++){
            int t1 = find_father(q[i].arr[j]);
            int t2 = find_father(q[i].arr[j - 1]);
            if(t1 != t2){
                fa[t1] = t2;
                now ++;
            }
        }
    }
    for(int i = 0 ; now < n - 1; i++){
         int   c = array[i];              /*找到边的编号*/
         int _t1 = line[c].l;             /*找到边的两个端点*/
         int _t2 = line[c].r;
         int t1 = find_father(_t1);
         int t2 = find_father(_t2);
         if(t1 != t2){
            cost += line[c].cost;
            fa[t1] = t2;
            now++;
         }
    }
    return cost;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        array.clear();
        LL _ans = 0;
        scanf("%d%d",&n,&m);
        init();
        for(int i = 0 ; i < m ; i++){
            scanf("%d%d",&q[i].size,&q[i].cost);
            for(int j = 0 ; j < q[i].size ; j ++)
                scanf("%d",&q[i].arr[j]);
        }
        line_size = 0;
        /*求出所有的边*/
        for(int i = 1 ; i <= n ; i++){
            scanf("%d%d",&p[i].x,&p[i].y);  /*输出坐标*/
            for(int j = 1 ; j < i ; j++){   /*求出1 ~ i - 1个点与i的距离*/
                line[line_size].l = j;
                line[line_size].r = i;
                line[line_size].cost = _dist(p[i],p[j]); /*求出2个点如果连接需要的花费*/
                line_size ++;
            }
        }
        /*找最短路,并记录最短路的每条边*/
        /*先排序*/
        sort(line,line + line_size);
        for(int i = 0 ,j = 0 ; j < n - 1; i ++){  /*当连线为n - 1的时候说明n个点都连通了*/
             int l = line[i].l , r = line[i].r;   /*找一个线段的2个端点*/
             int fa_l = find_father(l);           /*利用并查集找出其连通分量*/
             int fa_r = find_father(r);
             if(fa_l != fa_r){                    /*如果不在同一个连通分量*/
                fa[fa_l] = fa_r;                  /*合并!*/
                j ++;                             /*多加一条边*/
                _ans += line[i].cost;             /*费用*/
                array.push_back(i);               /*添加最小边*/
             }
        }
        /*二级制枚举子集!*/
        for(int i = 1 ; i < (1 << m) ; i++){
            LL ans = solve(i);
            _ans = min(ans,_ans);
        }
        printf("%lld\n",_ans);
        if(T > 0)
        printf("\n");
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值