最小树形图

模板题:poj3164

定义:一个有向图,存在从某个点开始的到达所有的的一个最小生成树,则它就是最小树形图。

朱-刘算法的大概过程如下:

1、找到除了root以为其他点的权值最小的入边。用In[i]记录

2、如果出现除了root以外存在其他孤立的点,则不存在最小树形图。

3、找到图中所有的环,并对环进行缩点,重新编号。

4、更新其他点到环上的点的距离

        假设有重新编号之前的节点u, v;编号之后为U, V

        则dis[U][V] = dis[u][v] - In[V];

 

 5、重复3,4知道没有环为止。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>

using namespace std;

const int maxn = 105;
const int inf = 1e9 + 7;

struct Q
{
    int u, v;
    double c;
}edge[maxn * maxn];

struct P
{
    double x, y;
    P(double a, double b)
    {
        x = a, y = b;
    }
    P(){}
}node[maxn];

double dis(P A, P B)
{
    return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}

int n, m;
double in[maxn];
int pre[maxn];
int vis[maxn], id[maxn];

double MST(int s, int n, int m)
{
    double res = 0;
    while(true)
    {/*****找所有点最小的入边*****/
        for(int i = 1; i <= n; i++) in[i] = inf;
        for(int i = 0; i < m; i++)
        {
            int u = edge[i].u, v = edge[i].v;
            double c = edge[i].c;
            if(c < in[v] && u != v)
            {
                pre[v] = u;
                in[v] = c;
            }
        }
        in[s] = 0;
        for(int i = 1; i <= n; i++)
        {
            if(in[i] == inf) return -1; //原图不连通
        }
    /**********判断有没有环******/
        memset(vis, -1, sizeof(vis));
        memset(id, -1, sizeof(id));
        int cnt = 1;
        for(int i = 1; i <= n; i++)
        {
            res += in[i];
            int v = i;
            while(vis[v] != i && v != s && id[v] == -1)
            {
                vis[v] = i;
                v = pre[v];
            }
            if(v != s && id[v] == -1)
            {
                for(int u = pre[v]; u != v; u = pre[u]) id[u] = cnt;
                id[v] = cnt++;
            }
        }
        if(cnt == 1)
        {
           break;
        }

        for(int i = 1; i <= n; i++)
        {
            if(id[i] == -1)
            {
                id[i] = cnt++;
            }
        }
        /***********建立新图*****************/
        for(int i = 0; i < m; i++)
        {
            int u = edge[i].u;
            int v = edge[i].v;
            edge[i].u = id[u];
            edge[i].v = id[v];
            if(id[u] != id[v]) edge[i].c -= in[v];
        }
        n = cnt - 1;
        s = id[s];
    }
    return res;
}

int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        for(int i = 1; i <= n; i++)
        {
           scanf("%lf %lf", &node[i].x, &node[i].y);
        }
        for(int i = 0; i < m; i++)
        {
            scanf("%d %d", &edge[i].u, &edge[i].v);
            edge[i].c = dis(node[edge[i].u], node[edge[i].v]);
            if(edge[i].u == edge[i].v) edge[i].c = inf;
        }
        double ans = MST(1, n, m);
        if(ans == -1) puts("poor snoopy");
        else printf("%.2f\n", ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值