并查集+最小生成树
链接:https://www.luogu.com.cn/problem/UVA1151
先过一次kruskal,然后枚举所有的可能性找最小值
代码:
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <iostream>
#define ll long long
using namespace std;
const int maxn=1050;
int fa[maxn];
struct Edge{
int u,v;
int w;
bool operator < (const Edge&ch )const
{
return w<ch.w;
}
}a[maxn*maxn];
struct node{
int x,y;
}v[maxn];
int total;
vector<int> g[10];
int V[maxn];
void init(){
for(int i=1; i<=maxn; i++) fa[i]=i;
}
int get(node a,node b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int Find(int x){
int r=x;
while(r!=fa[r]){
r=fa[r];
}
int j=x;
while(fa[j]!=r){
int i=fa[j];
fa[j]=r;
j=i;
}
return r;
}
int kruskal(){
int ss=0;
for(int i=1; i<=total; i++){
int fx=Find(a[i].u);
int fy=Find(a[i].v);
if(fx!=fy){
ss+=a[i].w;
fa[fx]=fy;
}
}
return ss;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int num;
int n,q;
memset(V,0,sizeof(V));
scanf("%d%d",&n,&q);
for(int i=0; i<=q; i++) g[i].clear();
for(int i=0; i<q; i++){
int x;
scanf("%d%d",&num,&V[i]);
for(int j=1; j<=num; j++){
scanf("%d",&x);
g[i].push_back(x);
}
}
for(int i=1; i<=n; i++){
scanf("%d%d",&v[i].x,&v[i].y);
}
total=0;
for(int i=1; i<=n; i++){
for(int j=i+1; j<=n; j++){
total++;
a[total].u=i; a[total].v=j;
a[total].w=get(v[i],v[j]);
}
}
init();
sort(a+1,a+1+total);
int sum=kruskal();
int c;
for(int i=0; i<(1<<q); i++){
init();
c=0;
for(int j=0; j<q; j++)
{
if(i & (1<<j)){//确保进行的次数只有(1<<q)次
c+=V[j];
int xx=Find(g[j][0]);
for(int k=1; k<g[j].size(); k++){
int xy=Find(g[j][k]);
if(xx!=xy){
fa[xy]=xx;
}
}
}
}
sum=min(c+kruskal(),sum);
}
printf("%d
",sum);
if(t) printf("
");
}
return 0;}