zoukankan      html  css  js  c++  java
  • 最小树形图 朱刘算法模板+建边技巧

    http://acm.hdu.edu.cn/showproblem.php?pid=4966

    把每个状态(LV,课程)当成一个点,全部连起来,然后给同一节课高等级的点向低等级连一条权值为0的边,最后创一个虚根连向所有的LV0,

    用一个最简单的进制映射,类似ax+b hash

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    using namespace std;
    const int maxn = 100000 + 100;
    const int INF = 2147480000;
    
    struct Edge {
        int from, to;
        long long w;
    }edges[maxn];
    int sz;
    int a[100], sum[100];
    int N, M;
    void add_edge(int from, int to, long long w) {
        ++sz;
        edges[sz].from = from; edges[sz].to = to; edges[sz].w = w;
    }
    int pre[maxn];//存储父节点
    int vis[maxn];//标记作用
    int id[maxn];//id[i]记录节点i所在环的编号
    int in[maxn];//in[i]记录i入边中最小的权值
    long long zhuliu(int root, int n, int m, Edge *edge)//root根 n点数 m边数
    {
        long long res = 0;
        int u, v;
        while (1)
        {
            for (int i = 0; i < n; i++)
                in[i] = INF;//初始化
            for (int i = 1; i <= m; i++)
            {
                Edge E = edge[i];
                if (E.from != E.to && E.w < in[E.to])
                {
                    pre[E.to] = E.from;//记录前驱
                    in[E.to] = E.w;//更新
                }
            }
            for (int i = 0; i <n; i++)
                if (i != root && in[i] == INF)
                    return -1;//有其他孤立点 则不存在最小树形图
                              //找有向环
            int tn = 0;//记录当前查找中 环的总数
            memset(id, -1, sizeof(id));
            memset(vis, -1, sizeof(vis));
            in[root] = 0;//
            for (int i = 0; i <n; i++)
            {
                res += in[i];//累加
                v = i;
                //找图中的有向环 三种情况会终止while循环
                //1,直到出现带有同样标记的点说明成环
                //2,节点已经属于其他环
                //3,遍历到根
                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] = tn;//记录节点所属的 环编号
                    id[v] = tn++;//记录节点所属的 环编号  环编号累加
                }
            }
            if (tn == 0) break;//不存在有向环
                               //可能存在独立点
            for (int i = 0; i <n; i++)
                if (id[i] == -1)
                    id[i] = tn++;//环数累加
                                 //对有向环缩点  和SCC缩点很像吧
            for (int i = 1; i <= m; i++)
            {
                v = edge[i].to;
                edge[i].from = id[edge[i].from];
                edge[i].to = id[edge[i].to];
                //<u, v>有向边
                //两点不在同一个环 u到v的距离为 边权cost - in[v]
                if (edge[i].from != edge[i].to)
                    edge[i].w -= in[v];//更新边权值 继续下一条边的判定
            }
            n = tn;//以环总数为下次操作的点数 继续执行上述操作 直到没有环
            root = id[root];
        }
        return res;
    }
    
    int main() {
        while (scanf("%d%d", &N, &M) != EOF && N&&M) {
            sz = 0;
            sum[0] = 0;
            for (int i = 1; i <= N; i++) {
                scanf("%d", &a[i]);
                sum[i] = sum[i - 1] + a[i] + 1;
            }
    
            int c, L1, d, L2;
            long long w;
            for (int i = 1; i <= M; i++) {
                scanf("%d%d%d%d%lld", &c, &L1, &d, &L2, &w);
                /* for(int j=L1;j<=a[c];j++){
                add_edge(sum[c-1]+j+1,sum[d-1]+L2+1,w);
                }*/
                add_edge(sum[c - 1] + L1 + 1, sum[d - 1] + L2 + 1, w);
            }
            for (int i = 1; i <= N; i++) {
                add_edge(0, sum[i - 1] + 1, 0);
            }
            for (int i = 1; i <= N; i++) {
                for (int j = 0; j<a[i]; j++) {
                    add_edge(sum[i - 1] + j + 2, sum[i - 1] + j + 1, 0);
                }
            }
            long long ans = zhuliu(0, sum[N] + 1, sz, edges);
            printf("%lld
    ", ans);
        }
        return 0;
    }
    成功的路并不拥挤,因为大部分人都在颓(笑)
  • 相关阅读:
    image
    MySQLdb
    【基础】Equal方法、面向对象-多态-继承-封装
    【转】SSL协议、SET协议、HTTPS简介
    【转】UML图与软件开发过程那点关系
    【转载】日本社会为啥没有“王思聪”
    调用0A中断输入字符串数据段的DUP定义
    字符串输入逆序输出(回车换行符)
    输入单个字符并输出
    仿射密码加密解密文件流
  • 原文地址:https://www.cnblogs.com/SuuT/p/9362248.html
Copyright © 2011-2022 走看看