题意:裸题。所谓最小树形图:给定一个带权有向图和一个固定结点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; }