Command Network poj 3164 (最小树形图)

来源:互联网 发布:linux配置ntp时间同步 编辑:程序博客网 时间:2024/06/11 07:26

题意:求有向的最小生成树,没有输出“poor …”。
我也是初学最小树形图,我看了别人的博客和一些资料,对于我这样的新手来说,我觉得如果我不去一步一步琢磨他们的代码,还真难理解,一说明他们写的不够详细,对新手来说没办法一下理解。我从新手的角度来重新写一遍,以我的表达能力可能不能确保能让你们完全理解,将就下拉。


最小树形图就是有向的最小生成树。
首先来想想,为什么我们没办法用prim,kruskal来做有向的最小生成树。首先prim,存在一个致命的问题,它用于存两点距离的数组只能存一个值,所以只能表示双向;kruskal它将最小的边一个接一个合并,有向图要考虑方向,而其算法必须方向一致。
那么最小树形图怎么求?
首先他除起始点外找到所有值最小的入度边,如果有点是没有入度边的话,那么证明这个点怎么也无法经过,那么就没有最小树形图了。
然后找环,如果没有环,那么就直接能连成一条最小树形图了,如果有环(比如:1->2, 2 ->3, 3->1),那么我们可以将环看成一个点,(重新编号时,一个环里的数编号一样),那么再把不是环的点,点到这个环,点到点 的最小距离更新下,再重复检查重新编号后有无环,直到无环。

#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<queue>#include<stack>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const double PI = acos(-1.0);const int mod = 1e9 + 7;const int N = 10010;int n,m;struct Point{    double x,y;}point[110];struct Ver{    int a,b;    double w;}ver[N];double Dis(Point a,Point b){    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));}double in[N];   //最小入度边int fa[N];      //它的前一个数,也就是指向它的数int ha[N],vis[N];  //ha[]是数的重新编号,vis[]是表示在i为起点时有无使用过。double zhuliu(int root){    double ans = 0;    while(1)    {        for(int i = 0; i < n; i++)            in[i] = INF;        memset(fa,-1,sizeof(fa));        for(int i = 0; i < m; i++)// 最小入度边        {//            if(ver[i].w == -1)//                continue;            int to = ver[i].b;            if(ver[i].w < in[to] && ver[i].a != ver[i].b)            {                in[to] = ver[i].w;                fa[to] = ver[i].a;            }        }        for(int i = 0; i < n; i++)        {            if(i != root && in[i] == INF)// 除根点,有点入度为0.                return -1;        }        int cnt = 0;        in[root] = 0;        memset(ha,-1,sizeof(ha));        memset(vis,-1,sizeof(vis));        for(int i = 0; i < n; i++)//找出环编号        {            ans += in[i];            int v = i;            while(v != root && ha[v] == -1 && vis[v] != i)            {                vis[v] = i;                v = fa[v];            }            if(v != root && ha[v] == -1)//是环,缩点            {                for(int j = fa[v]; j != v; j = fa[j])                    ha[j] = cnt;                ha[v] = cnt++;            }        }        if(cnt == 0)            break;        for(int i = 0; i < n; i++)            if(ha[i] == -1)    //不是环的还是-1                ha[i] = cnt++; //编号        for(int i = 0; i < m; i++) //更新其他点到环的最小距离        {            int temp = ver[i].b;            ver[i].a = ha[ver[i].a];            ver[i].b = ha[ver[i].b];            if(ver[i].a != ver[i].b)                ver[i].w -= in[temp];        }        n = cnt;        root = ha[root];    }    return ans;}int main(){    while(~scanf("%d%d",&n,&m))    {        memset(ver,0,sizeof(ver));        for(int i = 0; i < n; i++)            scanf("%lf%lf",&point[i].x,&point[i].y);        for(int i = 0; i < m; i++)        {            int from,to;            scanf("%d%d",&from,&to);            ver[i].a = from-1;            ver[i].b = to-1;            if(from == to)                ver[i].w = -1;            ver[i].w = Dis(point[from-1],point[to-1]);        }        double ans = zhuliu(0);        if(ans == -1)            puts("poor snoopy");        else            printf("%.2lf\n",ans);    }    return 0;}
0 0
原创粉丝点击