题意
:
NY在自己的花园里养了很多猫。有一天,一个巫婆在N个点设置了魔法,然后有M条关系,每一条在两个点之间有栅栏。
NY需要损坏这些栅栏但是需要栅栏长度这么多神奇的水,因为这种水很昂贵所以希望水用的越少越好。输出最少花费。
输入N,M表示N个点,接下来N行每行一个点的坐标,接下来M行每行两个数表示x,y之间有栅栏相连。
没有栅栏会交叉,每个圈都至少有一只猫。
分析:
题目意思就是如果图产生了圈就要把一些边去掉,破坏这个圈,问需要破坏的边的最小长度。
那么每次并查集的时候只要判断在同一个连通分量那么就需要破坏掉这条边,累加即可。因为不会有重边,所以
按边的权值从大到小,构成类似最小生成树,没加的边就是围成圈的最小边,即为题意重要用圣水抹去的边。
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stdio.h>
#include<queue>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
#define N 11000
struct Edge
{
int u,v;
double d;
}edge[N*N/2+10];
struct node
{
double x,y;
}p[N];
int n,m;
int father[N];
int cmp(struct Edge h,struct Edge f)
{
return h.d>f.d;
}
double Dist(node a,node b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int Find(int x)
{
while(x!=father[x])
x=father[x];
return x;
}
int main()
{
int i,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=1;i<=n;i++)
father[i]=i;
for(i=1;i<=n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
}
double sum=0.0; ///sum是巫婆围得总边和
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
edge[i].u=a;
edge[i].v=b;
edge[i].d=Dist(p[a],p[b]);
sum=sum+edge[i].d;
}
sort(edge,edge+m,cmp);
double s=0.0; ///记录不用抹去的边,类似Kruskal求最小生成树
for(i=0;i<m;i++)
{
int x,y;
x=Find(edge[i].u);
y=Find(edge[i].v);
if(x!=y)
{
if(x>y)
father[x]=y;
else
father[y]=x;
s=s+edge[i].d;
}
else
continue;
}
double ans=0.0;
ans=sum-s;
printf("%.3f\n",ans);
}
return 0;
}