379. 捉迷藏

题目链接

379. 捉迷藏

Vani 和 cl2 在一片树林里捉迷藏。

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

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

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

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

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

输入格式

输入数据的第一行是两个整数 NM

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

输出格式

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

数据范围

N200,M30000

输入样例:

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

输出样例:

3

解题思路

有向无环图的最小路径点覆盖

路径点覆盖:选择若干条不相交(点不相交)的路径,这些路径可以将所有的点覆盖
最小路径点覆盖:即路径最少的路径点覆盖

结论:=n
证明:首先要将一个有向无环图转化为一个二分图,不妨拆点:即将一个点拆分为入点和出点,将出点看成二分图的左部,入点看成二分图的右部,原图的所有边都可以转化为二分图的边,由于选择的路经是不相交的,即每个点的出度和入度至多为 1,对应在二分图上,即左部的点向右部连的边是唯一的,即是匹配边,对于一条路径的终点,其出度为 0,对应在二分图即左部的非匹配点,故 n 即为有向无环图的最小路径点覆盖

扩展:
结论:记原图 G,求传递闭包后的图记为 G,求 G 的最小路径重复点覆盖等价于求 G 的最小路径点覆盖
证明:
G 的路径点覆盖 G 的路径重复点覆盖
传递闭包即对于 x>y>z,这时 x>z,求出 G 的路径点覆盖后,凡是涉及 x>z 这样的边,都扩展开来,即对应 G 的路径重复点覆盖

G 的最小路径重复点覆盖 G 的最小路径点覆盖
对于当前路径 >x>p>y>,如果存在重复点 p,即前面有这样的路径 >a>p>b>,则当前路径可以跳过 p,因为 p 已经被选了,即对应 G 中含传递闭包的路径
故两者可以互相转换,求 G 的最小路径重复点覆盖即可转化为求 G 的最小路径点覆盖

本题要找最多的点,使得两两之间不存在可达的边,设 G 的最小路径重复点覆盖为 x,则要找的点不可能比 x 大,因为 G 的最小路径重复点覆盖包含了所有的点,且每一条路径上最多只能选择一个点,否则由鸽笼原理,一定会存在一条可达的路径,后面构造使得:=x,将最小重复点覆盖路径的终点的集合即为 E,设 E 中的所有点可到的点的集合为 next(E),如果 Enext(E)=,即 E 中所有的点都到不了 E 本身的某一个点,即 E 中所有点互不可达;如果 Enext(E),每次选择一条终点可到 E 的一条路径,不选这个点,往回退选点,所有这样的终点往回退选点,直到 Enext(E)= 为止,假设存在这样一条路径,其往回退退到起点依然所有的点仍在 next(E) 中,说明这条路径上的所有的点都可以被覆盖掉,这与最小路径重复点覆盖矛盾,所以这样的方法可行,即一定会有 Enext(E)=,故答案即为最小路径重复点覆盖

  • 时间复杂度:O(nm)

代码

// Problem: 捉迷藏 // Contest: AcWing // URL: https://www.acwing.com/problem/content/381/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=205; int n,m; bool g[N][N],st[N]; int match[N]; bool find(int x) { for(int i=1;i<=n;i++) if(g[x][i]) { if(!st[i]) { st[i]=true; if(match[i]==0||find(match[i])) { match[i]=x; return true; } } } return false; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); g[x][y]=true; } 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=n; for(int i=1;i<=n;i++) { memset(st,0,sizeof st); if(find(i))res--; } printf("%d",res); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16937058.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示