https://vjudge.net/problem/UVA-1151
题意:平面上有n个点,两点间的费用为两点的欧几里得距离。另外还有m个套餐,购买第i个套餐需要花费c[i],购买后套餐中的点均连通。
题解:先对原图求一次最小生成树,将原图的其他边都去掉,仅剩下这n-1条边。然后状态枚举[0,1<<m)进行购买套餐的操作,然后再求最小生成树即可。这种思路是正确的:用Kruskal算法求最小生成树时,就是每次优先选择最小的边,而购买套餐后,原图最小生成树这n-1条边仍然处于最优先考虑的位置。注意每个输出间还有空格。
代码:
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#define debug cout<<"aaa"<<endl
#define d(a) cout<<a<<endl
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define MIN_INT (-2147483647-1)
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807i64
#define MIN_LL (-9223372036854775807i64-1)
using namespace std;
const int N = 1000 + 5;
const int M = N * N + 5;
const int mod = 1000000000 + 7;
const double eps = 1e-8;
int x[N],y[N];
int F[N];
int Find(int x){
return x==F[x]?x:F[x]=Find(F[x]);
}
void unite(int x,int y){
x=Find(x),y=Find(y);
F[x]=y;
}
struct node{
int u,v;
int w;
node(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
}a[M],K[N];
struct Node{
vector<int> v;
int w;
}q[10];
int dis(int i,int j){
return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
bool cmp(node a,node b){
return a.w<b.w;
}
int main(){
int t,num,n,m,u,v,cnt,temp;
int x1,x2,y1,y2,w;
LL ans,minn;
scanf("%d",&t);
while(t--){
cnt=0,ans=MAX_INT;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
q[i].v.clear();
scanf("%d%d",&num,&q[i].w);
while(num--){
scanf("%d",&u);
q[i].v.pb(u);
}
}
for(int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
F[i]=i;
for(int j=1;j<i;j++){
a[++cnt]=node(i,j,dis(i,j));
}
}
sort(a+1,a+cnt+1,cmp);num=0;
for(int i=1;i<=cnt;i++){
u=a[i].u,v=a[i].v;
if(Find(u)!=Find(v)){
unite(u,v);
K[++num]=a[i];
if(num==n-1){
break;
}
}
}
for(int i=0;i<(1<<m);i++){
minn=0;
for(int j=1;j<=n;j++){
F[j]=j;
}
for(int j=0;j<m;j++){
if(i&(1<<j)){
minn+=q[j].w;
for(int k=1;k<q[j].v.size();k++){
unite(q[j].v[0],q[j].v[k]);
}
}
}
for(int j=1;j<=n-1;j++){
u=K[j].u,v=K[j].v;
if(Find(u)!=Find(v)){
unite(u,v);
minn+=K[j].w;
}
}
ans=min(ans,minn);
}
printf("%lld\n",ans);
puts("");
}
return 0;
}