• AcWing 379 捉迷藏


    \(AcWing\) \(379\) 捉迷藏

    ACwing 379. 捉迷藏 有向无环图的最小路径点(可重复)覆盖+传递闭包

    一、题目描述

    \(Vani\)\(cl2\) 在一片树林里捉迷藏。

    这片树林里有 \(N\) 座房子,\(M\) 条有向道路,组成了一张 有向无环图(\(DAG\))

    树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。

    如果从房子 \(A\) 沿着路走下去能够到达 \(B\),那么在 \(A\)\(B\) 里的人是能够相互望见的。

    现在 \(cl2\) 要在这 \(N\) 座房子里选择 \(K\) 座作为藏身点,同时 \(Vani\) 也专挑 \(cl2\) 作为藏身点的房子进去寻找,为了避免被 \(Vani\) 看见,\(cl2\) 要求这 \(K\) 个藏身点的任意两个之间都没有路径相连。

    为了让 \(Vani\) 更难找到自己,\(cl2\) 想知道最多能选出多少个藏身点。

    输入格式
    输入数据的第一行是两个整数 \(N\)\(M\)

    接下来 \(M\) 行,每行两个整数 \(x,y\),表示一条从 \(x\)\(y\) 的有向道路。

    输出格式
    输出一个整数,表示最多能选取的藏身点个数。

    数据范围
    \(N≤200,M≤30000\)
    输入样例

    7 5
    1 2
    3 2
    2 4
    4 5
    4 6
    

    输出样例:

    3
    

    二、最小路径覆盖

    最小路径点覆盖

    概念
    对于一个有向无环图,用最少的互不相交的路径将所有的点覆盖。(这里的最少的互不相交的路径是指:边不重复,点也不重复)。

    拆点:对于原图中\((1, n)\)拆为新图中的\((1', n')\)

    转化:将原图中的\(i -> j\) 转化为新图中的\(i -> j'\)

    如图将原图中的\(1 -> 2 -> 3\)转化为新图的路径即为:

    \(1 -> 2', 2 -> 3'\)

    但是不能出现\(1 -> 2, 3 -> 2\)这样点就重复了

    因此得出两个结论:

    • 1:路径 $\Leftrightarrow $ 匹配

    • 2:左部非匹配点 $\Leftrightarrow $ 路径终点,孤立的点也算一种特别的终点,比如\(5\)

    所以要求原图中最少的互不相交的路径,即原图中终点最少的路径,即求新图中左侧最少的非匹配点的数量\((n - m)\),即求新图中左侧最大的匹配数量(\(m\)),即求最大匹配数(\(m\)), 最后用左侧所有点数(\(n\)) - 最大匹配数(\(m\))即为最少的互不相交的路径

    在二分图中,最小路径点覆盖=总点数-最大匹配数

    最小路径点 重复 覆盖

    概念
    给定一张有向无环图,要求使用尽量少的可相交的简单路径,覆盖有向无环图的所有顶点(也就是每个顶点可以覆盖多次)。这个问题被称为有向无环图的 最小路径可重复点覆盖

    定理一

    有向无环图\(G\)的最小路径点覆盖包含的路径条数
    等于 \(n\) 减去 拆点二分图\(G2\) 的最大匹配数
    \(G2\)左部点代表着每条有向边起点,右部点代表每条有向边终点。

    定理二

    有向无环图\(G\)的最小路径可 重复点覆盖 包含的路径条数
    求解步骤:
    ① 先对有向图传递闭包得到\(G'\)
    ② 再进行最小路径点覆盖的操作

    四、本题思路

    这是一个 最小路径可重复问题, 首先要进行 传递闭包 将其 转换为不可重复问题

    传递闭包

    通俗的讲就是如果\(a->b, b->c\),那么我们就建立一条\(a->c\)的边。将所有能间接相连的点直接相连。 \(Floyd\)能在\(O(n^3)\) 求出一个图的传递闭包。

    将原图进行传递闭包之后图会额外增加右图的边

    那么新图(左\(+\)\(=\)新图)中的 最小路径点覆盖问题 其实就是 左图中 重复覆盖所有点最少的路径数量

    因为每个路径终点不会 相互到达,所以 路径数就等于终点数, 现在想求 最少的终点数,也就是求 最少的路径数

    因此,这些终点就是保证互相到达不了的能够选取的 藏身点个数

    为什么这个问题是一个最小路径可重复覆盖? 因为题目中有说,如果一个点沿着某一路径走下去可以到达另一个点,则这两个点也是互相可以望见的,也就是说对于边\(a->b,b->c\),间接的\(a->c\)也算。所以就先\(floyd\)求一下传递闭包,建立拆点二分图,然后跑一遍 最小路径点覆盖

    五、实现代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 210, M = 30010;
    int n, m;
    int g[N][N], st[N];
    int match[N];
    
    bool find(int x) {
        for (int i = 1; i <= n; i++) {
            if (g[x][i] && !st[i]) {
                st[i] = true;
                int t = match[i];
                if (t == -1 || find(t)) {
                    match[i] = x;
                    return true;
                }
            }
        }
        return false;
    }
    
    int main() {
        scanf("%d %d", &n, &m);
        memset(match, -1, sizeof match);
    
        while (m--) {
            int a, b;
            scanf("%d %d", &a, &b);
            g[a][b] = 1;
        }
    
        // floyd求传递闭包
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    g[i][j] |= g[i][k] & g[k][j];
    
        int res = 0;
        for (int i = 1; i <= n; i++) {
            memset(st, 0, sizeof st);
            if (find(i)) res++;
        }
        printf("%d\n", n - res);
        return 0;
    }
    

    同类题

  • 相关阅读:
    【问题 & 解决】VS Code 添加第三方库提示
    最大公约数、最小公倍数、辗转相除法的求解和证明
    移动APP开发框架盘点2:Web移动前端框架大全
    你好,2021
    reacthookform 使用小结
    github查看仓库的clone量
    neo4j切换数据库方法,简单好用!知识图谱
    数据平滑处理均值|中值|SavitzkyGolay滤波器
    2021年总结
    使用vscode Container开发调试envoy
  • 原文地址:https://www.cnblogs.com/littlehb/p/16102845.html
Copyright © 2020-2023  润新知