这是一道最小树形图的模板题
朱刘算法开始时的确不是太好理解,在网上看了好多文章才差不多理解。
在这里说一点,缩点时,如果弧(u,v)的v点在一个环中,这个环形成的缩点在新图中的编号是k,那么新图中(u,k)的权值是W(u,v)-in[v],因为ret(即最终的返回值)只在朱刘算法的开始置了一次0,这个权值的变化可以保证,在对一个点加入新的入边时,可以顺便把上一次的旧边的权值在ret中去掉,对于不在环上的点,也是这样
代码如下(模板来自kuangbin)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 105
#define maxm 10005
using namespace std;
struct Edge
{
int u, v;
double cost;
};
Edge edge[maxm];
int pre[maxn], vis[maxn],id[maxn];
double in[maxn],xy[maxn][2];
inline double cal_dis(int i,int j)
{
return sqrt(pow(xy[i][0] - xy[j][0], 2) + pow(xy[i][1] - xy[j][1], 2));
}
//root为根的序号,n为点数,m为有效的边的数量(即去除掉自环)
double liuzhu(int root, int n, int m)
{
int v,tn;
double ret = 0;
while (1)
{
for (int i = 1; i <= n; i++)
in[i] = -1;//-1标志该入边不存在
for(int i=0;i<m;i++)
if (edge[i].cost < in[edge[i].v]|| in[edge[i].v]<0)
{
in[edge[i].v] = edge[i].cost;
pre[edge[i].v] = edge[i].u;
}
for (int i = 1; i <= n; i++)
if (i != root && in[i]<0)
return -1;
tn = 0;
in[root] = 0;
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
for (int i = 1; i <= n; i++)
{
ret += in[i];
v = i;
while (vis[v] != i && id[v] == -1&&v!=root)
{
vis[v] = i;
v = pre[v];
}
if (id[v] == -1 && v != root)
{
tn++;//这里点的序号从1开始,所以tn也要先加
for (int u = pre[v]; u != v; u = pre[u])
id[u] = tn;
id[v] = tn;
}
}
if (!tn) break;
for (int i = 1; i <= n; i++)
if (id[i] == -1)
id[i] = ++tn;
for (int i = 0; i < m;)
{
v = edge[i].v;
edge[i].u = id[edge[i].u];
edge[i].v = id[edge[i].v];
if (edge[i].u != edge[i].v)
edge[i++].cost -= in[v];
else
swap(edge[i], edge[--m]);//如果是环中的边,抛弃该边,并把总边数-1
}
n = tn;
root = id[root];
}
return ret;
}
int main()
{
int n, m, lcnt;
double t;
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = 1; i <= n; i++) scanf("%lf%lf", &xy[i][0], &xy[i][1]);
lcnt = 0;//用于统计有效的弧的数量
for (int i = 0,u,v; i < m; i++)
{
scanf("%d%d", &u, &v);
if (u != v)
{
edge[lcnt].u = u;
edge[lcnt].v = v;
edge[lcnt++].cost = cal_dis(u, v);
}
}
if (lcnt < n - 1) t = -1;//如果非自弧的数量<n-1,肯定无法形成树形图
else t = liuzhu(1, n, lcnt);
if (t < 0) printf("poor snoopy\n");
else printf("%.2lf\n", t);
}
return 0;
}