题目:http://poj.org/problem?id=3164
题意:有n个点,给出这n个点的坐标。然后有m对点,意为可以在两点之间连一条有向边,花费为两点距离。问能不能连一些边使从1出发可以到达其他所有点,若可以求出最小花费
思路:最小树形图模板题。首先为除根之外的每点选定一条权值最小的入边,如果这个入边集不存在有向环的话,该图的最小树形图。如果存在有向环的话,将这个有向环缩成一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。然后可以证明,新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。
//复杂度O(nm)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
const double eps = 1e-8;
struct edge
{
int v, u;
double cost;
}g[N*N*2];
int pre[N], id[N], vis[N];
double x[N], y[N], in[N];
int n, m;
double ma(int s, int n, int m)//s为根,n为点数,m为边数,点和边均从0开始
{
double res = 0;
while(true)
{ //统计每个点的权值最小的入边,并记录前驱节点
for(int i = 0; i < n; i++) in[i] = INF;
for(int i = 0; i < m; i++)
{
int v = g[i].v, u = g[i].u;
if(g[i].cost < in[u] && v != u) in[u] = g[i].cost, pre[u] = v;
}
for(int i = 0; i < n; i++)
{//检查除根外的其余点是否都有入边,若某点无入边必定无树形图
if(i == s) continue;
if(fabs(in[i] - INF) < eps) return -1;
}
int num = 0; //用于缩点编号
memset(id, -1, sizeof id);//记录每个点缩点后的编号
memset(vis, -1, sizeof vis);//找环过程中的标记数组
in[s] = 0;
for(int i = 0; i < n; i++)
{
res += in[i];
int v = i;
//找环,每个点沿着前驱往前走,要么走到根,要么找到环
while(vis[v] != i && id[v] == -1 && v != s) vis[v] = i, v = pre[v];
if(v != s && id[v] == -1)
{//找到环,缩点
for(int j = pre[v]; j != v; j = pre[j]) id[j] = num;
id[v] = num++;
}
}
if(num == 0) break;//无环,已求出最小树形图
for(int i = 0; i < n; i++)//对不在环中的其他点编号
if(id[i] == -1) id[i] = num++;
for(int i = 0; i < m; i++)
{//建立新图
int v = g[i].v, u = g[i].u;
g[i].v = id[v], g[i].u = id[u];
if(id[v] != id[u]) g[i].cost -= in[u];
}
n = num, s = id[s];
}
return res;
}
double dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2));
}
int main()
{
while(~ scanf("%d%d", &n, &m))
{
for(int i = 0; i < n; i++) scanf("%lf%lf", &x[i], &y[i]);
for(int i = 0; i < m; i++)
{
scanf("%d%d", &g[i].v, &g[i].u);
g[i].v--, g[i].u--;
if(g[i].v != g[i].u) g[i].cost = dis(x[g[i].v], y[g[i].v], x[g[i].u], y[g[i].u]);
else g[i].cost = INF;
}
double res = ma(0, n, m);
if(fabs(-1 - res) < eps) printf("poor snoopy\n");
else printf("%.2f\n", res);
}
return 0;
}