• 最小树形图


    主要用途:

    求有向图的最小生成树。

    时间复杂度O(nm)

     

     两张图,(好像全网都是这两张图)

    模板:

    #include"stdio.h"
    #include"string.h"
    #include"algorithm"
    using namespace std;
    const int INF = 1e9 + 7;
    const int N = 20010;
    
    typedef struct Node{
        int u,v,w;
    }Node;
    
    int n,m,r;
    int ans,vis[N],pre[N],id[N];///vis表示该点有无被使用,pre表该点前缀,falg表该点还有无
    int dis[N];
    Node node[N];
    
    
    int directed_mst(int root){///求以root结点为root的最小树形图的权值
        int ans = 0;
        while(true){
            for(int i = 1; i <= n; i ++)
                dis[i] = INF;
            for(int i = 1; i <= m; i ++){///找到每个结点的最小前驱
                int u = node[i].u,v = node[i].v;
                if(u == v || node[i].w >= dis[v]) continue;
                dis[v] = node[i].w;pre[v] = u;
            }
            for(int i = 1; i <= n; i ++)///判断有误解,图是否联通
               if(i != root && dis[i] == INF) return -1;
            int cnt = 0;
            for(int i = 1; i <= n; i ++) vis[i] = id[i] = 0;
            for(int i = 1; i <= n; i ++){
                if(i == root) continue;
                ans += dis[i];
                int v = i;
                while(vis[v] != i && !id[v] && v != root)///找环
                {
                    vis[v] = i;
                    v = pre[v];
                }
                if(!id[v] && v != root){
                    id[v] = ++ cnt;///把环上的点都标记为同一点。
                    for(int u = pre[v]; u != v; u = pre[u])
                        id[u] = cnt;
                }
            }
            if(cnt == 0) break;///无环,得到解
            for(int i = 1; i <= n; i ++)
                if(!id[i]) id[i] = ++ cnt;
            for(int i = 1; i <= m; i ++){
                int u = node[i].u,v = node[i].v;
                node[i].u = id[u],node[i].v = id[v];
                if(id[u] != id[v]) node[i].w -= dis[v];
            }
            root = id[root];
            n = cnt;
        }
        return ans;
    }
    int main()
    {
         scanf("%d%d%d",&n,&m,&r);
         for(int i = 1; i <= m; i ++)
         {
              scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w);
         }
         int ans = directed_mst(r);
         printf("%d
    ",ans);
         return 0;
    }
    

    同时如果求的是无根树的树形图

    1,构造0结点

    2,0结点向各个结点连接一个sum+1的权值边

    3,如果最后最小树形图的总权值大于2*(sum+1)则表明图不联通,无解,如果小于的话,将ans-(sum+1)即是答案

    如果要输出编号最小的根节点,多解的情况一定是有环,而超级源点最后选择的一条出边一定就是最优解(多加的边按照点的编号排序);所以我们就在找边的时候加上一句就好了;

    if (u == root) pos = i;
    

     那么答案即为pos-m-1(pos为边的编号)

  • 相关阅读:
    C++纯虚函数调用
    C++编译期数据结构 在不同的地方声明全局数组的初始值
    用虚拟机把ubuntu安装到TF卡上
    点击a链接打开第三方页面变成下载页面问题
    SRAM和DRAM的区别
    真值表生成逻辑表达式程序
    大端 小端
    vhdl rising_edge(clk) (clk'event and clk='1')的区别
    ROM、RAM、DRAM、SRAM、FLASH的区别
    同一个主机头的多域名绑定多个ssl证书的方法
  • 原文地址:https://www.cnblogs.com/yrz001030/p/12370206.html
Copyright © 2020-2023  润新知