379 捉迷藏(最小路径重复点覆盖)

2023-11-22 10:59

本文主要是介绍379 捉迷藏(最小路径重复点覆盖),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 问题描述:

Vani 和 cl2 在一片树林里捉迷藏。这片树林里有 N 座房子,M 条有向道路,组成了一张有向无环图。树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。如果从房子 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
来源:https://www.acwing.com/problem/content/description/381/

2. 思路分析:

分析题目可以知道已知一个有向图(DAG),让我们从中选择最多的点使得任意两点不能够从一个点走到另外一个点,也即任意两点不可达,并且路径之间允许有交集的,也即可以存在公共点,属于最小路径重复点覆盖问题,最小路径重复点覆盖问题属于最小路径点覆盖问题的一个扩展,最小路径点覆盖又称为最小路径覆盖,是针对于有向无环图来说的,我们希望使用最少的互不相交的路径将所有的点覆盖住(路径中的点是不能够重复的),最小路径点覆盖需要到拆点,这个拆点的方式比较独特:

原图中存在i-->j的路径则i向j'连一条边,连的边是有向的,但是在具体实现的时候看成是无向边,得到的新图一定是二分图,下面是一个有向无环图的例子:

在二分图中最小路径点覆盖 = 总点数 - 最大匹配数(res  = n - m),这里的总点数是指左部和右部的总点数之和,每一个点最多有一个出度和入度,每一个点至多在一条路径中,所以一定是一个匹配,其中存在两个关键点:

  • 新图中的路径等价于匹配
  • 路径终点等价于左部的非匹配点

原图中求解最少互不相交的路径等价于求解新图中左部的最少的非匹配点的数量,等价于让左侧的非匹配点数量最少,等价于让左侧的匹配点最多,等价于在新图中找最大匹配,新图中左部的总点数减去最大匹配的数量就是最小路径点覆盖。而对于最小路径重复点覆盖,则是最小路径点覆盖的一个扩展,允许路径覆盖点的路径有公共点,最小路径重复点覆盖的的求解主要有两个步骤:

  • 求解传递闭包G'(原图为G),传递闭包:如果i-->k有路径,k->j有路径那么i向j一条边
  • 在新图G'求解最小路径覆盖

也即原图中的最小路径重复点覆盖等价于在新图中求解最小路径点覆盖,其实可以发现这两者是一一对应的关系,对于原图中任意一种覆盖方式都可以转化为新图中不重复的覆盖方式,并且需要的路径数量是相等的,同理右边的覆盖方式也可以对应坐标的覆盖方式,所以原图的路径重复点覆盖等于新图的路径覆盖,最终的答案为最小路径重复点覆盖的数量 = n - 新图中的最大匹配,这个结论的证明还是比较复杂的。如最小路径重复点覆盖有cnt条,那么说明cnt条路线可以将所有的点覆盖住,而我们需要在这里cnt路线上选,并且每一条路径最多只能够选择一个点,也即答案小于等于cnt,接下来就是构造一种方案使得选出的点的数量的等于cnt,说明答案就是cnt。构造的比较独特,这里就没有写如何构造了,知道有一种构造的方式使得最小路径重复点覆盖的数量等于cnt就可以了。

3. 代码如下:

from typing import Listclass Solution:# 在新图上求解最小路径覆盖因为求解最小路径覆盖是需要拆点的, 但是在实际的过程中我们并没有拆点, 其实在做完floyd算法之后其实是可以看成拆成了两个点, floyd算法求解完传递闭包之后那么每一个间接到达的边都有一条直接连在一起的边def find(self, u: int, n: int, st: List[int], match: List[int], d: List[List[int]]):for i in range(1, n + 1):if st[i] == 0 and d[u][i] == 1:st[i] = 1if match[i] == -1 or self.find(match[i], n, st, match, d):match[i] = ureturn Truereturn Falsedef process(self):n, m = map(int, input().split())        #  因为后面在floyd算法的时候只需要判断两点之间是否有边即可, 不涉及到权重所以在原数组中进行操作也是没有影响的g = [[0] * (n + 10) for i in range(n + 10)]for i in range(m):x, y = map(int, input().split())# 有向图g[x][y] = 1# 题目中的测试数据都是以1开始编号的for k in range(1, n + 1):for i in range(1, n + 1):for j in range(1, n + 1):# k是i->j的中间点那么i->j连一条边g[i][j] |= 1 if g[i][k] and g[k][j] else 0match = [-1] * (n + 10)res = 0for i in range(1, n + 1):st = [0] * (n + 10)if self.find(i, n, st, match, g):res += 1# 二分图中总点数 - 最大匹配 = 最小路径点覆盖return n - resif __name__ == "__main__":print(Solution().process())

这篇关于379 捉迷藏(最小路径重复点覆盖)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/409674

相关文章

hdu2544(单源最短路径)

模板题: //题意:求1到n的最短路径,模板题#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#i

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.

poj3261(可重复k次的最长子串)

题意:可重复k次的最长子串 解题思路:求所有区间[x,x+k-1]中的最小值的最大值。求sa时间复杂度Nlog(N),求最值时间复杂度N*N,但实际复杂度很低。题目数据也比较水,不然估计过不了。 代码入下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

poj 2175 最小费用最大流TLE

题意: 一条街上有n个大楼,坐标为xi,yi,bi个人在里面工作。 然后防空洞的坐标为pj,qj,可以容纳cj个人。 从大楼i中的人到防空洞j去避难所需的时间为 abs(xi - pi) + (yi - qi) + 1。 现在设计了一个避难计划,指定从大楼i到防空洞j避难的人数 eij。 判断如果按照原计划进行,所有人避难所用的时间总和是不是最小的。 若是,输出“OPETIMAL",若

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回