zoukankan      html  css  js  c++  java
  • P4716 【模板】最小树形图

      一道模版题(就是这个算法有点偏……)

      这道题就是在一个有向图中,求出一个定根的有根树,使其边权之和最小,其实就是有向图的最小生成树。

      其实挺简单的……没我想象的那么高深。就是在改边权的地方有点不好理解,正确性可以用数学归纳法证明。

      一次次缩点直到这个图不再有环为止。

      代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<climits>
    using namespace std;
    #define maxn 20000
    #define inf 999999999
    struct node
    {
        int from,to,w;
    } edge[maxn];
    int in[maxn],pre[maxn],id[maxn];
    int vis[maxn];
    int n,m,root,cnt;
    void add(int a,int b,int w)
    {
        edge[++cnt].to=b;
        edge[cnt].from=a;
        edge[cnt].w=w;
    }
    int zhuliu()
    {
        int ans=0;
        while(1)
        {
            for(int i=1; i<=n; i++)
                in[i]=inf;
            for(int i=1; i<=m; i++)
            {
                int u=edge[i].from;
                int v=edge[i].to;
                if(u!=v&&edge[i].w<in[v])
                {
                    in[v]=edge[i].w;
                    pre[v]=u;
                }
            }
            for(int i=1; i<=n; i++)
                if(i!=root&&in[i]==inf) return -1;
            int num=0;
            memset(vis,0,sizeof(vis));
            memset(id,0,sizeof(id));
            for(int i=1;i<=n;i++)
            {
                if(i==root) continue;
                ans+=in[i];
                int v=i;
                while(vis[v]!=i&&id[v]==0&&v!=root)
                {
                    vis[v]=i;
                    v=pre[v];
                }
                if(v!=root&&id[v]==0)
                {
                    id[v]=++num;
                    for(int j=pre[v];j!=v;j=pre[j])
                        id[j]=num;
                } 
            }
            if(!num) break;
            for(int i=1;i<=n;i++)
                if(!id[i]) id[i]=++num;
            for(int i=1;i<=m;i++)
            {
                int u=edge[i].from;
                int v=edge[i].to;
                edge[i].from=id[u];
                edge[i].to=id[v];
                if(id[u]!=id[v]) edge[i].w-=in[v];
            }
            root=id[root];
            n=num;
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&root);
        for(int i=1; i<=m; i++)
        {
            int a,b,w;
            scanf("%d%d%d",&a,&b,&w);
            add(a,b,w);
        }
        printf("%d
    ",zhuliu());
        return 0;
    }

       补充:

      有几个地方很重要不可以丢掉,比如找环的时候判断id和根节点。判断id是因为这条边可能会连到一个环里,而这个环已经被染色,不能再染一遍。判断根是因为我们认为根节点没有入边,因为根节点是被指定的树根。并且不用担心从不属于这个环的点连到环里使解错误,因为就算回到环里,也不会回到这个点而是会会到与这个点直接相连的那个环里的点,从这个点反而把环处理了一遍。而这个帮别人忙的点,我们一会儿整体扫一遍的时候会发现到,将被视为单点一个环。

  • 相关阅读:
    使用批处理查找某个文件夹及其子文件夹(递归地)中存在的空文件夹
    pl/sql连接oracle
    JSON对象转换成url参数
    创建maven工程
    线程同步
    java.lang.Object 的 wait 和 notify方法,及关键字 synchronized的作用
    Thread操作
    java.lang.io包的使用
    用程序打印汉字
    java程序员应该熟练掌握的技术
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/10952818.html
Copyright © 2011-2022 走看看