捉迷藏 最小重复点覆盖

一、内容

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

二、思路

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

三、代码

#include <cstdio>
#include <cstring>
using namespace std;
const int N = 205;
int n, m, u, v, g[N][N], mat[N];
bool vis[N];
bool dfs(int u) {
	for (int v = 1; v <= n; v++) {
		if (!g[u][v] || vis[v]) continue; vis[v] = true;
		if (!mat[v] || dfs(mat[v])) {
			mat[v] = u; return true;
		}
	}
	return false;
} 
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &u, &v); g[u][v];
	}
	//求传递闭包
	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 ans = n;
	//求出最大匹配 
	for (int i = 1; i <= n; i++) {
		memset(vis, false, sizeof(vis));
		ans -= dfs(i);
	} 
	printf("%d\n", ans);
	return 0;
} 
发布了456 篇原创文章 · 获赞 466 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_41280600/article/details/104463150