UVA 1151Buy or Build MST

来源:互联网 发布:云朵课堂下载软件 编辑:程序博客网 时间:2024/06/06 16:42

题意:

             平面上有n个点,要让所有n个点都连通,所以你要构造一些边来连通他们,连通的费用等于两个端点的欧几里得距离的平方。另外还有q个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci。求最小花费。


思路:

         在这里我们可以采取枚举所有可能 + K算法来得出答案,比如这里有三个套餐,我们利用二进制枚举001、010、011 、100、 101、 110、 111 分别代表第一个和第二个不要,要第三个(001);不要第一个和第三个,要第二个(010).......即 0 代表不要, 1 代表要,然后把要的套餐中的所有点都连通,再用K算法求剩下的未连接的点的最小生成树。


注意:

         在套餐中合并点时不能单纯地让pre[i] = pre[1](i > 1);pre[i]数组代表 pre[i] 和 i 在一个集合里面(并查集);举个栗子:

有一个套餐是

4 10  2 3 4 5

含义是购买这个套餐中可以让四个点连通,分别是2,3,4,5号点,费用为 10;如果让 pre[3] = pre[4]=pre[5] = 2;

那么假设还有个套餐:

3 9 1 5 3 

含义如上 ,如果再写pre[5] = pre[3] = 1;那么假设我购买了这俩个套餐,本应该2 3 4 5 1都在一个集合里面的,但是按照上面那么写 则 2 4 是一个集合, 1 3 5 是一个集合。不符合我的意思,所以购买套餐合并里面的点时应该写成pre[i] = Find(pre[1]);前提是这俩个不在一个集合里面。


代码:

<pre name="code" class="cpp">#include <bits/stdc++.h>  #define prln(x) cout<<(x)<<endl  using namespace std;  typedef long long LL;    const double PI = acos(-1);  const double ESP = 1e-8;  const int MAXN = 1000 + 3;  const int MOD = 1e9 + 7;  int pre[MAXN];    typedef struct Point{ //题目中给的点      int x;      int y;  }Po;    typedef struct Buy{  //套餐      int m;      //购买该套餐可以合并点的个数      int ci;       //购买该套餐的费用      int a[MAXN];   //这个套餐可以合并的点的编号      int flag;    //是否要购买这个套餐,对每个套餐的这个值进行二进制枚举  }Bu;    typedef struct City{   //用来存储图      int u;      int v;      int w;  }Ci;    Ci edge[MAXN * MAXN / 2 + 3];  Po pt[MAXN];  Bu buy[11];    int Find(int x) //并查集  {      return x == pre[x] ? x : pre[x] = Find(pre[x]);  }    void Stpre(int n)  {      for(int i = 0; i <= n; i++)          pre[i] = i;  } void Ststu(){        memset(&pt,0,sizeof(Po));        memset(&buy,0,sizeof(Bu));        memset(&edge,0,sizeof(Ci));}int Ojld(Point a, Point b){    int xx = a.x - b.x;    int yy = a.y - b.y;    return xx * xx + yy *yy;}int mycmp(City a, City b){    return a.w < b.w;}int ksu(int l)//K算法{    int ans= 0;    for(int i = 1; i<= l; i++)    {        int fv = Find(edge[i].v);        int fu = Find(edge[i].u);        if(fu != fv)        {            pre[fu] = pre[fv];            ans += edge[i].w;        }    }    return ans;}int main(){    //freopen("input.txt","r",stdin);    int t;    cin >> t;    while(t--)    {        Ststu();        int n, q;        scanf("%d%d",&n, &q);        for(int i = 1; i <= q; i++)        {            scanf("%d",&buy[i].m);            scanf("%d",&buy[i].ci);            for(int j = 1; j <= buy[i].m; j++)                scanf("%d",&buy[i].a[j]);        }        for(int i = 1; i <= n; i++)            scanf("%d%d",&pt[i].x, &pt[i].y);        int sum = 0;        for(int i = 1; i < n; i++)        {            for(int j = i + 1; j <= n; j++)            {                sum++;                edge[sum].u = i;                edge[sum].v = j;                edge[sum].w = Ojld(pt[i], pt[j]);                //printf("%d %d %d %d\n",sum,i,j,edge[sum].w);            }        }        sort(edge + 1, edge + sum + 1 , mycmp);        int ans =  0x7F7F7F7F;        for(int i = 0; i < (1 << q); i++) //二进制枚举        {            Stpre(n);            int temp = i;            int mst = 0;            for(int j = 1; j <= q; j++)            {                if(temp & 1)                {                    mst += buy[j].ci;                    for(int k = 2; k <= buy[j].m; k++)                    {                        int fx = Find ( buy[j].a[1] );                        int fy = Find( buy[j].a[k] );                        if(fy != fx)                          pre[fy] = pre[fx];                    }                }                temp >>= 1;            }            mst += ksu(sum);            ans = min(ans, mst);        }        printf("%d\n",ans);        if(t)prln("");    }    return 0;}


                                             
0 0
原创粉丝点击