A - Buy or Build UVA - 1151

题意:给你n个点和一些集合,在集合中的点是相互联通的,现在需要修建一些路使得n个点相互联通,其中每条路你可以修建也可以买,如果修建的话花费是两个点之间的欧几里得距离,其中点是在二维坐标中。每个点的位置都给出了,如果要修建的这条边在某个集合中,你也可以选择把这个集合买下来,每个集合的花费也给出了。

思路:先根据每个点的最坐标求出两点之间的欧几里得距离,再在每两点之间修建道路,先不买任何集合求出此时最小的花费,此时就是求图的最小生成树,然后在二进制枚举,因为已经告诉你集合的个数不超过8个,所以可以进行二进制枚举,看买那些边,如果要是买下其中一些集合的话,使用并查集把这些点都加到一起。然后在根据把边加到一起之后在求最小生成树,这时求最小生成树时就爱直接根据不买任何集合时的最小生成树求,因为求最小的花费所使用的肯定是最小生成树里的一些边。

#include <bits/stdc++.h>

using namespace  std;
const int maxn=1e6+50;
int n,m;
int cnt;
int c;
struct Point
{
    int x,y;
} p[maxn];
struct Node
{
    int x,y,w;
} node[maxn],mst[maxn];
struct Team
{
    int val,num;
    int v[maxn];
} sub[10];
int fa[maxn];
void Init()
{
    for(int i=0; i<=n; i++)
        fa[i]=i;
}
long long int Dig(int x,int y)
{
    long long   int xx=p[x].x-p[y].x;
    long long  int yy=p[x].y-p[y].y;
    return (long long int)(xx*xx+yy*yy);
}
int Find_x(int x)
{
    return fa[x]=(x==fa[x]?x:Find_x(fa[x]));
}
long long int solve()
{
    long long int res=0;
    for(int i=0; i<c; i++)
    {
        int rx=Find_x(mst[i].x);
        int ry=Find_x(mst[i].y);
        if(rx==ry) continue;
        else
        {
            res+=mst[i].w;
            fa[rx]=fa[ry];
        }
    }
    return res;
}
bool cmp(Node n1,Node n2)
{
    return n1.w<n2.w;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
//        if(T>0) cout<<endl;
        scanf("%d%d",&n,&m);
        Init();
        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&sub[i].num,&sub[i].val);
            for(int j=0; j<sub[i].num; j++)
                scanf("%d",&sub[i].v[j]);
        }
        for(int i=1; i<=n; i++)
            scanf("%d%d",&p[i].x,&p[i].y);
        cnt=0;
        c=0;
        for(int i=1; i<=n; i++)
        {
            for(int j=i+1; j<=n; j++)
            {
                node[cnt].x=i;
                node[cnt].y=j;
                node[cnt].w=Dig(i,j);
                cnt++;
            }
        }
        long long int sum=0;
        sort(node,node+cnt,cmp);
        for(int i=0; i<cnt; i++)
        {
            int rx=Find_x(node[i].x);
            int ry=Find_x(node[i].y);
            if(rx!=ry)
            {
                fa[rx]=ry;
                sum+=node[i].w;
                mst[c++]=node[i];
            }
        }
//        cout<<"c "<<c<<endl;
        long long int res=0;
        for(int s=0; s<(1<<m); s++)
        {
            Init();
            res=0;
            for(int i=0; i<m; i++)
            {
                if(s&(1<<i))
                {
                    res+=sub[i].val;
                    for(int j=1; j<sub[i].num; j++)
                    {
                        fa[Find_x(sub[i].v[j])]=Find_x(sub[i].v[j-1]);/////////
                    }
                }
            }
            res+=solve();
            sum=min(sum,res);
        }
        cout<<sum<<endl;
        if(T) cout<<endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值