题意:裸题。所谓最小树形图:给定一个带权有向图和一个固定结点n,求从n出发能到达所有点的最小权生成树。
思路:朱刘算法,最坏情况复杂度位O(VE)。后有优化算法降低了复杂度。
算法步骤如下:(本文不再证明,参考下面给出的我自己画的一个图即可理解)
1.判断图的连通性,若不连通直接无解,否则一定有解。
2.为除了根节点以外的所有点选择一个权值最小的入边,假设用pre数组记录前驱,f数组记录选择的边长,记所选边权和为temp。
3.(可利用并查集)判断选择的的边是否构成环,若没有则直接ans+=temp并输出ans,若有,则进行下一步操作。
4.对该环实施缩点操作,设该环上有点V1,V2……Vi……Vn,缩成的点为node ,对于所有不在环中的点P进行如下更改:
(1) 点P到node的距离为min{a[p,Vi]-f[Vi]} (a为边集数组)
(2)点node到p的距离为min{a[Vi,p]}
操作(1)的理解:先假设环上所有边均选上,若下次选择某一条边进入该环,则可以断开进入点与进入点的前驱之间的边,即断开F[进入点],所以等效为直接把a[p,node]赋值为min{a[p,Vi]-f[Vi]}。
特别提醒:本题有自环,可以提前删掉,因为它没有用。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define clr(s,t) memset(s,t,sizeof(s));
#define INF 0x3fffffff
#define N 105
#define M 10005
struct point{
double x,y;
}p[N];
struct edge{
int x,y,next;
double w;
}e[M];
int first[N],top,n,m,used[N],f[N],pre[N],root;
double in[N];
double dist(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void add(int x,int y,double w){
e[top].x = x;
e[top].y = y;
e[top].w = w;
e[top].next = first[x];
first[x] = top++;
}
double zhu_liu(){
int i,num,a,b;
double res = 0;
root = 1;
while(1){
for(i = 1;i<=n;i++)
in[i] = INF;
for(i = 0;i<top;i++) //遍历每个点,寻找权值最小的入边
if(e[i].w < in[e[i].y] && e[i].x!=e[i].y){
in[e[i].y] = e[i].w;
pre[e[i].y] = e[i].x;
}
for(i = 1;i<=n;i++)//如果有除root的点没有入边,则必无最小树形图
if(i != root && in[i] == INF)
return 0;
clr(f, -1);
clr(used, -1);
num = in[root] = 0;
for(i = 1;i<=n;i++){
res += in[i]; //结果累加
b = i;
while(used[b]!=i && b!=root && f[b]==-1){
//找圈,从当前点往回找只有三种可能,找到根、找到另外一个已经找到的圈,或者形成一个新圈
used[b] = i;
b = pre[b];
}
if(b!=root && f[b]==-1){//标记新找到的圈
f[b] = ++num;
for(a = pre[b];a!=b;a=pre[a])
f[a] = num;
}
}
if(!num) //若无圈,则直接返回
break;
for(i = 1;i<=n;i++)
if(f[i] == -1)
f[i] = ++num;
for(i = 0;i<top;i++){ //缩圈为点
if(f[e[i].x] != f[e[i].y])
e[i].w -= in[e[i].y];
e[i].x = f[e[i].x];
e[i].y = f[e[i].y];
}
n = num;
root = f[root];
}
return res;
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
int i,a,b;
double tmp;
top = 0;
clr(first, -1);
for(i = 1;i<=n;i++)
scanf("%lf %lf",&p[i].x,&p[i].y);
for(i = 1;i<=m;i++){
scanf("%d %d",&a,&b);
if(a!=b)
add(a,b,dist(p[a],p[b]));
}
if(n==1){
printf("0.00\n");
continue;
}
tmp = zhu_liu();
if(tmp)
printf("%.2lf\n",tmp);
else
printf("poor snoopy\n");
}
return 0;
}