最小树形图

最小树形图---有向图的最小生成树(与无向图不一样QAQ)

朱刘算法(必须从0开始存)

POJ 3164 模板题

#include<bits/stdc++.h>
using namespace std;

const int inf = 0x3f3f3f3f;
struct node
{
    int x,y;
    double z;
}t[100100];
int xi[1100],yi[1100],pre[1100],id[1100],vis[1100];
double in[1100];
int n,m,x,y,z;
double ans;

double zhuliu(int root,int n)
{
    double res = 0;   
    while(1)
    {
        for(int i = 0;i < n; ++i) in[i] = inf;  //存最小边权
        for(int i = 0;i < m; ++i)  //遍历每条边
            if(t[i].y != t[i].x && in[t[i].y] > t[i].z)  
            {
                pre[t[i].y] = t[i].x;
                in[t[i].y] = t[i].z;
            }
        for(int i = 0;i < n; ++i)
            if(i != root && in[i] == inf) return -1;  //除了根以外有点没有入边
        int tot = 0;  //找环
        memset(id,-1,sizeof(id));
        memset(vis,-1,sizeof(vis));
        in[root] = 0;
        for(int i = 0;i < n; ++i)
        {
            res += in[i];
            int v = i;
            while(vis[v] != i && id[v] == -1 && v != root)
            {
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && id[v] == -1)
            {
                for(int u = pre[v]; u != v;u = pre[u])
                    id[u] = tot;
                id[v] = tot++;
            }
        }
        if(tot == 0) break;  //建立新图,缩点重新编号
        for(int i = 0;i < n; ++i)
            if(id[i] == -1) id[i] = tot++;
        for(int i = 0;i < m; ++i)
        {
            int u = t[i].x;
            int v = t[i].y;
            t[i].y = id[v];
            t[i].x = id[u];
            if(t[i].x != t[i].y) t[i].z -= in[v];
        }
        n = tot;
        root = id[root];
    }
    return res;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 0;i < n; ++i)
            scanf("%d%d",&xi[i],&yi[i]);
        for(int i = 0;i < m; ++i)
        {
            scanf("%d%d",&x,&y);
            x--; y--;
            t[i].x = x;
            t[i].y = y;
            t[i].z = sqrt((xi[x] - xi[y]) * (xi[x] - xi[y])
                               + (yi[x] - yi[y]) * (yi[x] - yi[y]));
        }
        ans = zhuliu(0,n);
        if(ans == -1) printf("poor snoopy\n");
        else printf("%.2f\n",ans);
    }
    return 0;
}

HDU 2121

无根的最小树形图问题

虚拟一个超级源点,建一条比所有边总和sum多1的边,看最后的结果是否大于2*(sum+1),不是的话就是原图有最小树形图,是的话就代表没有

#include<bits/stdc++.h>
using namespace std;

const int inf = 0x3f3f3f3f;
struct node
{
    int x,y;
    double z;
}t[100100];
int pre[1100],id[1100],vis[1100],in[1100];
int n,m,x,y,z,ans,sum,minr;

double zhuliu(int root,int n)
{
    double res = 0;
    while(1)
    {
        for(int i = 0;i < n; ++i) in[i] = inf;
        for(int i = 0;i < m; ++i)
            if(t[i].y != t[i].x && in[t[i].y] > t[i].z)
            {
                pre[t[i].y] = t[i].x;
                in[t[i].y] = t[i].z;
                if(t[i].x == root) minr = i;
            }
        for(int i = 0;i < n; ++i)
            if(i != root && in[i] == inf) return -1;
        int tot = 0;
        memset(id,-1,sizeof(id));
        memset(vis,-1,sizeof(vis));
        in[root] = 0;
        for(int i = 0;i < n; ++i)
        {
            res += in[i];
            int v = i;
            while(vis[v] != i && id[v] == -1 && v != root)
            {
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && id[v] == -1)
            {
                for(int u = pre[v]; u != v;u = pre[u])
                    id[u] = tot;
                id[v] = tot++;
            }
        }
        if(tot == 0) break;
        for(int i = 0;i < n; ++i)
            if(id[i] == -1) id[i] = tot++;
        for(int i = 0;i < m; ++i)
        {
            int u = t[i].x;
            int v = t[i].y;
            t[i].y = id[v];
            t[i].x = id[u];
            if(t[i].x != t[i].y) t[i].z -= in[v];
        }
        n = tot;
        root = id[root];
    }
    return res;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        sum = 0;
        for(int i = 0;i < m; ++i)
        {
            scanf("%d%d%d",&x,&y,&z);
            t[i].x = x;
            t[i].y = y;
            t[i].z = z;
            sum += z;
        }
        sum++;
        for(int i = 0;i < n; ++i)
        {
            t[m].x = n;
            t[m].y = i;
            t[m++].z = sum;
        }
        ans = zhuliu(n,n + 1);
        minr -= (m - n);
        if(ans == -1 || ans >= 2 * sum) printf("impossible\n\n");
        else printf("%d %d\n\n",ans - sum,minr);
    }
    return 0;
}

定根与不定根的思考

情况1:    图不需要自己额外建(即题目给好了N个点,M条边),只是没有给定根.

解:添加一个点作为根,向每个节点都连接一条边,而边的权值需要比其他所有边的权值加起来更大,表明这些点都有可能是最小树形图的根, (至于为什么权值应该是所有边权值加起来,个人是这样认为的:首先,权值不能为0,权值为0的话最小树形图就始终会是全部取与虚根相连的边构建成的最小树形图,而当权值取其他所有之和时,可以更加方便的判断是否能生成最小树形图),在得出答案后减去虚根相连边的权值即是答案.

 

要做的题

CodeForces - 240E UVA - 11865  HYSBZ - 4349 HDU - 4966  HDU - 4009 HDU - 2121

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值