• 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;
    }
    

    同类题

  • 相关阅读:
    文件输入输出的管理以及管道的使用
    文件权限的管理以及acl权限列表
    安装配置Linux Squid代理服务器
    安全强化机制——SELinux
    存储系统管理(二)——Linux系统的swap分区、磁盘加密、磁盘阵列
    存储系统管理(三)——磁盘配额及lvm逻辑卷管理
    存储系统管理(一)——Linux系统的设备和分区管理
    core--线程同步
    Debian字符模式下修改显示分辨率
    core--作业
  • 原文地址:https://www.cnblogs.com/littlehb/p/16102845.html
Copyright © 2020-2023  润新知