题目: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;  
}