https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3592
题目大意:平面上有
n
n
n个点,你的任务是让所有n个点连通,你可以新建一些边,费用等于两个端点的欧几里得距离的平方。另外还有
q
q
q个套餐可以购买,如果你购买了第
i
i
i个套餐,该套餐中的所有结点将变得相互连通,该套餐的花费为
c
i
c_{i}
ci。求最小花费。
思路:任意两点之间连边(边权为欧几里得距离的平方),然后枚举选取套餐的情况,暴力跑克鲁斯卡尔算法就行了。因为格式错误 W A WA WA了好久- -。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1005;
struct Edge
{
int u,v,dis;
Edge(){}
Edge(int a,int b,int c)
{
u=a,v=b,dis=c;
}
bool operator <(const Edge &a)const
{
return dis<a.dis;
}
}edge[maxn*maxn];
int f[maxn],x[maxn],y[maxn];
int q[10][maxn],cost[10],len[10];
int n,m,t,cnt;
void init()
{
for(int i=1;i<=n;i++)
f[i]=i;
}
int father(int x)
{
return f[x]==x?x:f[x]=father(f[x]);
}
int dist(int a,int b)
{
return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]);
}
int cal()
{
int ans=0;
for(int i=0;i<cnt;i++)
{
int fx=father(edge[i].u);
int fy=father(edge[i].v);
if(fx!=fy)
{
ans+=edge[i].dis;
f[fx]=fy;
}
}
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&len[i],&cost[i]);
for(int j=0;j<len[i];j++)
scanf("%d",&q[i][j]);
}
for(int i=1;i<=n;i++)
scanf("%d%d",&x[i],&y[i]);
cnt=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
edge[cnt++]=Edge(i,j,dist(i,j));
sort(edge,edge+cnt);
int ans=2e9,ansx=0;
int times=1<<m;
for(int i=0;i<times;i++)
{
ansx=0;
init();
for(int j=0;j<m;j++)
{
if(i&(1<<j))
{
ansx+=cost[j];
for(int k=1;k<len[j];k++)
f[father(q[j][k])]=father(q[j][k-1]);
}
}
ansx+=cal();
ans=min(ans,ansx);
}
printf("%d\n",ans);
if(t)
printf("\n");
}
return 0;
}