UVA 1151

/*
    题意:有n个点,现在需要联通所有,有q种套餐可以选择,
    当然套餐之外也可以自己添加边,意为达到最短距离。

    题意很明显,不知道需要使用哪一种套餐,
    那么需要枚举每一种套餐的情况。
    然后再进行对比。
    注意最开始没有套餐的情况。
*/

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int maxn = 1100;

class Cor
{
public:
    int x,y;
}cor[maxn];             //坐标

class Node
{
public:
    int x,y;
    int dis;
}edge[maxn*maxn];       //边

vector<int>t[10];
int cost[10];   //每中套餐的花费
int n,m,q;
int f[maxn];

bool cmp(Node a,Node b)
{
    return a.dis < b.dis;
}

double distances(Cor a,Cor b)
{
    int dx = a.x - b.x;
    int dy = a.y - b.y;
    return (dx*dx + dy*dy);
}

void inti(int n)
{
    for(int i = 0;i <= n; i++)
        f[i] = i;
    return ;
}

int Find(int x)
{
    int r = x;
    while(r != f[r])
        r = f[r];
    int i = x,j;
    while(f[i] != r){
        j = f[i];
        f[i] = r;
        i = j;
    }
    return r;
}

int merge(int x,int y)
{
    int a = Find(x);
    int b = Find(y);
    if(a == b)
        return false;
    else
        f[a] = b;
    return true;
}

int kruskal()       //标准的kruskal
{
    int pos = 0,sum = 0;
    for(int i = 0;i < m && pos < n - 1; i++){
        if(merge(edge[i].x,edge[i].y)){
            pos++;
            sum += edge[i].dis;
        }
    }
    return sum;
}

void solve()
{
    inti(n);
    int ans = kruskal();            //初始的情况
    for(int i = 0;i < (1<<q); i++){
        int money = 0;
        inti(n);
        for(int j = 0;j < q; j++){  //枚举每一种套餐的情况
         //   if((i>>j)&1) continue;
            if(i&(1<<j)){
                money += cost[j];
                for(int k = 1;k < t[j].size(); k++){
                    merge(t[j][k],t[j][0]); //把套餐的点相连
                }
            }
        }
        ans = min(ans,kruskal() + money);
    }
    printf("%d\n",ans);
    return ;
}

int main()
{
   // freopen("in.txt","r",stdin);
    int ncase,pn = 1;
    scanf("%d",&ncase);
    while(ncase--){
        if(pn > 1)      //UVA的输出
            printf("\n");
        pn++;
        for(int i = 0;i < 10; i++)  //要清理,初始化
            t[i].clear();
        scanf("%d%d",&n,&q);
        for(int i = 0;i < q; i++){  //vector是把每一种套餐记录,
            //            因为不知道每一种套餐有几个点,所以用vector很合适
            int num,temp;
            scanf("%d%d",&num,&cost[i]);
            for(int j = 0;j < num; j++){
                scanf("%d",&temp);
                t[i].push_back(temp);
            }
        }
        for(int i = 1;i <= n; i++)
            scanf("%d%d",&cor[i].x,&cor[i].y);
        m = 0;
        for(int i = 1;i <= n; i++){
            for(int j = i + 1;j <= n; j++){
                edge[m].x = i;
                edge[m].y = j;
                edge[m++].dis = distances(cor[i],cor[j]);//计算每一条边的长度
            }
        }
        sort(edge,edge+m,cmp);  //kruskal特有
        solve();
    }
    return 0;
}
时间: 2024-10-12 07:57:43

UVA 1151的相关文章

UVa 1151 - Buy or Build(最小生成树)

链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3592 题意: 平面上有n个点(1≤n≤1000),你的任务是让所有n个点连通.为此,你可以新建一些边,费用等于两个端点的欧几里德距离的平方.另外还有q(0≤q≤8)个"套餐"可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通.第i个套餐的花费为C

UVa 1151 买还是建

https://vjudge.net/problem/UVA-1151 题意: 平面上有n个点,你的任务是让所有n个点连通.为此,你可以新建一些边,费用等于两个端点的距离平方和.另外还有q个套餐可以购买,如果你购买了第i个套餐,该套餐中的所有结点都变得相互连通,第i个套餐的花费为Ci. 思路: 这道题比较容易超时.可能需要用到并查集的路径压缩,我下面的代码就是用了路径压缩,不然要超时.也是看了别人的代码才知道还有这种省时间的做法. 先介绍一下路径压缩吧: 如果并查集像一字长蛇这样排列的话,寻找起

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

题意:给定n个点,你的任务是让它们都连通.你可以新建一些边,费用等于两点距离的平方(当然越小越好),另外还有几种“套餐”,可以购买,你购买的话,那么有些边就可以连接起来, 每个“套餐”,也是要花费的,让你求出最少花费. 析:首先想到的是把所有情况都考虑算一下,然后找出最少的,先算没有“套餐”的,然后算有的,用二进制枚举的话,总时间复杂度为O(2qn2+n2logn),这个时间复杂度太大了吧,肯定会超时, 那么我们就可以优化一下,首先先算出来最小生成树,并且把每条边都保存下来,那么加了“套餐”之后

【UVA 1151】 Buy or Build (有某些特别的东东的最小生成树)

[题意] 平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方. 另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci. 求最小花费. Input (1 ≤ n ≤ 1000)  (0 ≤ q ≤ 8). The second integer is the the cost of the subnetwork(not greater

UVa 1151 Buy or Build【最小生成树】

题意:给出n个点的坐标,现在需要让这n个点连通,可以直接在点与点之间连边,花费为两点之间欧几里得距离的平方,也可以选购套餐,套餐中所含的点是相互连通的 问最少的花费 首先想kruskal算法中,被加入的边已经是最优的了,所以当选择完套餐后,之前被丢弃的边也不会再进入最小生成树 然后就可以先求一次原图的最小生成树,保存下进入最小生成树的n-1条边 再枚举选择的套餐的情况,再求最小生成树,这里用的二进制法枚举 最后维护一个最小值就可以了 思路虽然看懂了,可是代码根本就写不出来,看着标程写的,最后还是

Buy or Build UVA - 1151

题意:有n个点,q个套餐,求连接所有的点的最小花费. 题解:用二进制枚举每个套餐,然后跑Kruskal().....这种写法应该会超时的,可能数据太水了.... 1 #include<vector> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int maxn=1

uva 1151(最小生成树,枚举子集)

题意:平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方.另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci. kruskal: 先求一次原图的最小生成树,得到n-1条边,然后每次枚举完套餐后只考虑套餐中的边和这n-1条边,则枚举套餐之后再求最小生成树. key: kruskal算法中,那些两端已经属于同一个连通分量的边不会再加到

UVA 1151 Buy or build(并查集之六)

题意:n个结点,以及m个组合,可以任意选择.求最小花费. 因为组合最多只有8个,可以枚举. #include<stdio.h> #include<math.h> #include<algorithm> #include<string.h> #define MAX 1007 #define INTMAX (int)1e9 using namespace std; struct list_buy { int num; int price; int node[MA

【uva 1151】Buy or Build(图论 最小生成树)

题意:平面上有N个点(1≤N≤1000),若要新建边,费用是2点的欧几里德距离的平方.另外还有Q个套餐,每个套餐里的点互相联通,总费用为Ci.问让所有N个点连通的最小费用.(2组数据的输出之间要求有换行) 解法:利用二进制枚举套餐,时间复杂度是O(2QN2+N2logN).关于时间复杂度,枚举:二进制枚举为2Q,Kruskal为ElogE≍E≍N2:边排序:ElogE≍E≍N2.总的相加. 紫书上提到一个优化:不加任何套餐跑一遍MST(最小生成树),没有选的边便删除掉,因为以后加了套餐之后也选不