题目传送门

同质题

本题的数学证明

个人理解,目前\(NOIP\)级别的选手,不用玩数字证明吧,能记下结论就很牛\(X\)了~

一、概念与定理

最小路径覆盖:用最少互不相交的路径,将点全部覆盖。

最小路径重复覆盖:用最少多少条路径覆盖所有点,路径可以有公共点和公共边

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

在\(DAG\)图\(G\)中的最小路径重复覆盖==在他的传递闭包图\(G'\)中的最小路径覆盖

二、套路与方法

最小路径重复覆盖 = 点数 - 最大匹配数,要用\(floyd\)求闭包,再跑匈牙利求最大匹配。

二、本题思路

每条路径上的任意点都可以相互看到。那么我们可以求出这个图的最小路径重复点覆盖的条数\(cnt\)。 每条路径上我们至少可以选出一个点。 故藏身点的数量就等于 最小路径重复点覆盖的条数。

三、实现代码

#include <bits/stdc++.h>
using namespace std;
const int N = 207, M = 30007;
int n, m;
bool d[N][N], st[N];
int match[N];

bool find(int x) {
for (int i = 1; i <= n; i++) {
if (d[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() {
cin >> n >> m;
memset(match, -1, sizeof match);
// memset(d,false,sizeof d);

for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
d[a][b] = true;
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] |= d[i][k] & d[k][j];

int res = 0;
for (int i = 1; i <= n; i++) {
memset(st, 0, sizeof st);
if (find(i)) res++;
}
cout << n - res << endl;
return 0;
}