uva1151 最小生成树kruskal

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<iostream>
using namespace std;
const int maxn=1e6+5;///这里数组要开大一点,不然re虽然不知道为什么。。。
int fa[maxn],n,q,casenum,r[maxn],nodenum,point,cnt,cas=1;
long long cost,price,price2;
struct node{
  int x,y;
};
node no[maxn];
struct edge{
  int st,en;
  long long cost;
};
edge e[maxn];

struct plan{
  int cost;///套餐的花费
  vector<int> v;///套餐中包含的点
};
plan p[9];

bool cmp(edge a,edge b){
  return a.cost<b.cost;
}

void init(){
    for(int i=1;i<=n;i++){
      fa[i]=i;///初始化编号
      r[i]=0;
    }
}

int found(int a){
   return a==fa[a]?a:fa[a]=found(fa[a]);
}

void unite(int a,int b){
    a=found(a);
    b=found(b);
   if(a!=b){
     if(r[a]>r[b])
        fa[b]=a;
     else{
        fa[a]=b;
        if(r[a]==r[b]){
            r[b]++;
        }
     }
   }
}

long long kruskal(){
 int cnt2=0;
 long long ans=0;
 sort(e+1,e+cnt,cmp);
 for(int i=1;i<cnt;i++){
    if(found(e[i].st)!=found(e[i].en)){
        unite(e[i].st,e[i].en);
        cnt2++;
        ans=ans+e[i].cost;
        if(cnt2==n-1)
            break;
    }
 }
 return ans;
}

void allEdge(){///求出所有点间的边
  cnt=1;
  for(int i=1;i<=n-1;i++)
    for(int j=i+1;j<=n;j++){
    e[cnt].st=i;
    e[cnt].en=j;
    e[cnt++].cost=(no[i].x-no[j].x)*(no[i].x-no[j].x)+(no[i].y-no[j].y)*(no[i].y-no[j].y);///注意这里不用开方
  }
}

int main(){
  scanf("%d",&casenum);
  while(casenum--){
    if(cas++>1) printf("\n");///UVA独特的输出格式
    scanf("%d%d",&n,&q);
    for(int i=1;i<=q;i++){
        scanf("%d%d",&nodenum,&p[i].cost);
        p[i].v.clear();
        for(int j=1;j<=nodenum;j++){
            scanf("%d",&point);
            p[i].v.push_back(point);
        }
    }
    for(int i=1;i<=n;i++){
        scanf("%d%d",&no[i].x,&no[i].y);
    }
    allEdge();
    init();///时刻注意init
    price=kruskal();
    //cout<<price<<endl;
    for(int i=0;i<(1<<q);i++){
        cost=0;
        init();
        for(int j=0;j<q;j++){
            if(i&(1<<j)){
                cost=cost+p[j+1].cost;
                for(int k=0;k<p[j+1].v.size()-1;k++)
                unite(p[j+1].v[k],p[j+1].v[k+1]);
            }
        }
            price2=kruskal()+cost;
            price=min(price2,price);
    }
    printf("%lld\n",price);
  }
  return 0;
}
思路:先对原图进行一次最小生成树的计算,算出不买任何套餐下的最小花费。然后,再枚举每一个套餐子集(二进制枚举),枚举的套餐的点用并查集把二者连接起来(这样kruskal建树包括这些点),然后比较求出最小值就可以了。特别要注意uva特殊的输出格式,即每两个连续的数据间的输出用空行隔开。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值