• 题解【LOJ10068】「一本通 3.1 练习 3」秘密的牛奶运输


    题面

    题目就是要我们求出图中严格次小生成树的边权之和。

    我们首先可以发现一个很显然的性质:严格次小生成树与最小生成树有且只有一条边不同。

    我们先求出图的最小生成树,然后枚举每一条非树边,尝试用它来替换树上的某一条边,使得我们新得到的树是图的严格次小生成树。

    具体地说,我们设当前枚举到的非树边的两个端点分别是 (u)(v),它们之间的距离是 (w),最小生成树的边权总和是 (sum)

    那么我们的答案就是 (min{sum-w+dist_{u,v}})。其中 (dist_{u,v})(u)(v) 在最小生成树上最大的边权。

    这个可以通过 DFS 预处理得到。

    注意在求答案时一定要先判断 (w) 是否大于 (dist_{u,v})

    我们注意到还有一个问题:如果 (u)(v) 的最大边权与 (w) 相等时怎么办?

    其实这个问题也不能难解决:我们只要再预处理一下 (u)(v) 的次大边权,转移时再判断 (w) 是否大于次大边权即可。

    记得开 long long。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 503, M = 20003;
    
    int n, m;
    struct Node
    {
        int u, v, w;
        bool fl; //fl 为 true 表示是树边,否则就是非树边
    } a[M];
    int tot, head[N], ver[M], nxt[M], edge[M];
    int dist[N][N], dist1[N][N]; //两点之间的最大距离和次大距离
    int fa[N];
    LL res, sum, ans;
    
    inline void add(int u, int v, int w)
    {
        ver[++tot] = v, edge[tot] = w, nxt[tot] = head[u], head[u] = tot;
    }
    
    int getf(int u)
    {
        if (fa[u] == u) return u;
        return fa[u] = getf(fa[u]);
    }
    
    inline bool cmp(Node x, Node y)
    {
        return x.w < y.w;
    }
    
    void dfs(int rt, int u, int f, int maxd, int max2d)
    {
        dist[rt][u] = maxd, dist1[rt][u] = max2d;
        for (int i = head[u]; i; i = nxt[i])
        {
            int v = ver[i], w = edge[i];
            if (v == f) continue;
            int maxxd = maxd, maxx2d = max2d;
            if (w > maxxd) maxxd = w; //更新最大值
            else if (w < maxxd && w > maxx2d) maxx2d = w; //一定要注意判断 w 是否小于最大值后再更新次大值
            dfs(rt, v, u, maxxd, maxx2d);
        }
    }
    
    int main()
    {
        cin >> n >> m;
        for (int i = 1; i <= m; i+=1)
        {
            cin >> a[i].u >> a[i].v >> a[i].w;
        }
        sort(a + 1, a + 1 + m, cmp);
        for (int i = 1; i <= n; i+=1) fa[i] = i;
        for (int i = 1; i <= m; i+=1)
        {
            int u = getf(a[i].u), v = getf(a[i].v);
            if (u != v)
            {
                fa[u] = v;
                sum += a[i].w; 
            	add(a[i].u, a[i].v, a[i].w);
           	 	add(a[i].v, a[i].u, a[i].w); //建立最小生成树
              	a[i].fl = true;
            }
        }
        for (int i = 1; i <= n; i+=1) dfs(i, i, -1, 0, 0);
        ans = 1000000000000000000;
        for (int i = 1; i <= m; i+=1)
            if (!a[i].fl)
            {
                int u = a[i].u, v = a[i].v;
                if (a[i].w > dist[u][v]) //大于最大边权
                    ans = min(ans, sum - dist[u][v] + a[i].w);
                else if (a[i].w > dist1[u][v]) //大于次大边权
                    ans = min(ans, sum - dist1[u][v] + a[i].w);
            }
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    第 13 章 外观模式
    第 12 章 桥接模式
    第 10 章 适配器模式
    第 7 章 原型模式
    PHP的预处理语句的使用
    PHP的异常处理
    PHP中PDO函数的使用
    PHP的PDO模式数据操作
    PHP的 Final关键字、类(文件)的加载和命名空间
    PHP的魔术常量和魔术方法
  • 原文地址:https://www.cnblogs.com/xsl19/p/12398497.html
Copyright © 2020-2023  润新知