#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
typedef double type;
const int maxn=205;
const double inf=1.0*0x3f3f3f3f;
struct point
{
double x, y;
}p[maxn];
struct node
{
int u, v;
type w;
}edge[maxn * maxn];
int pre[maxn], id[maxn], vis[maxn], n, m;
type in[maxn];
double dis(point a, point b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
type mst(int root, int nn, int mm)
{
type ret = 0;
while(true)
{
for(int i = 0; i < nn; i++) in[i] = inf; //1.找最小入边
for(int i = 0; i < mm; i++)
{
int u = edge[i].u, v = edge[i].v;
if(edge[i].w < in[v] && u != v) {pre[v] = u; in[v] = edge[i].w;}
}
for(int i = 0; i < nn; i++)
{
if(i == root) continue;
if(in[i] == inf) return -1;//除了跟以外有点没有入边,则根无法到达它
}
int cnt = 0; //2.找环
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
in[root] = 0;
for(int i=0;i<nn;++i) //标记每个环
{
ret+=in[i];
int v= i;
while(vis[v]!=i&&id[v]==-1&&v!=root) //每个点寻找其前序点,要么最终寻找至根部,要么找到一个环
{
vis[v] = i; v = pre[v];
}
if(v != root && id[v] == -1)//缩点
{
for(int u = pre[v]; u != v; u = pre[u]) id[u] = cnt;
id[v] = cnt++;
}
}
if(cnt == 0) break; //无环 则break
for(int i = 0; i < nn; i++) if(id[i] == -1) id[i] = cnt++;
for(int i = 0; i < mm; i++) //3.建立新图
{
int u = edge[i].u,v = edge[i].v;
edge[i].u = id[u];
edge[i].v = id[v];
if(id[u] != id[v]) edge[i].w -= in[v];
}
nn = cnt; root = id[root];
}
return ret;
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF)
{
for(int i = 0; i < n; i++) scanf("%lf%lf", &p[i].x, &p[i].y);
for(int i = 0; i < m; i++)
{
scanf("%d%d", &edge[i].u, &edge[i].v);
edge[i].u--; edge[i].v--;
if(edge[i].u != edge[i].v) edge[i].w = dis(p[edge[i].u], p[edge[i].v]);
else edge[i].w = inf; //去除自环
}
type ans = mst(0, n, m);
if(ans == -1) printf("poor snoopy\n");
else printf("%.2f\n", ans);
}
return 0;
}
最开始的图,把所有的最小入边都累加到ret里。至于为什么,因为这样才能保证所得的ret有可能是最小树形图的解,当然,是在这些最小入边集合不行成环得情况下。
如果有了环,ret肯定不是最终答案,因为环中间有的边需要删掉,而且环之间也要连接起来。现在我们无法得知删除环中的哪些边才行。这就需要建立新图了。
举个例子:某个图的部分图中, 1->2权值为3, 2->1权值为4, 3->1权值为9, 4->2权值为7。 那么可以看到,结点1和结点2是形成了一个环的。我们仅从其大小不知道删除哪条边比较好,这时看到3->1权值为9, 如果走这条边,那么接下来只能删除掉2->1这条边,同理走4->2的话就要删除掉1->2这条边。 那么就不妨建立新图, 将1和2缩成一点,3->1的权值就变成了9-4=5, 4->2的权值变成了7-3=4。 这样的话,就相当于变相删除了不需要走的边了。形成新图后,又变成了最小树形图的求解,就这样循环下去,直到图中的最小边集没有环为止。