#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特殊的输出格式,即每两个连续的数据间的输出用空行隔开。
uva1151 最小生成树kruskal
最新推荐文章于 2022-02-09 21:32:53 发布