ch6092 捉迷藏(DAG最小路径可重复点覆盖)

思路:

为什么这个题目的正解是DAG的最小路径可重复点覆盖呢???博主郁闷了两天(其实是打了两天游戏),突然从游戏里获得了答案(其实是游戏打的菜,被虐了,还是遇到了主播,,,自闭了,就来学了会儿)

不多BB,正文从此刻开始。 


首先明确,求解的是一个最大的点集,满足集合中的点中任意两个点之间没有通路。ohhhh???这不是最大独立集吗???可惜这是个有向图,最大独立集也是针对无向图来说的,如果你去找二分图的定义,会发现,前面都有一个前提,一个无向图怎样怎样,也就是说二分图是对无向图而言的。那这个有向图就不能当作最大独立集考虑了。

但是我们知道有向图有最小路径点覆盖,定义如下:选取最少的不相交的边覆盖全部顶点。最小路径可重复覆盖:选取最少可相交的边覆盖全部顶点。对于这个路径的集合,每次从里面最多挑出来一个点,如果挑出来多余一个点,以两个为例,那么这两个点之间肯定有一条简单路径可以连接,也就是说二者肯定有一方可以到达另一方。那么我们从所有的路径中,每个路径挑一个点,最后就组成了这个最大的点集合。

还有一点需要说明,为什么这个问题是一个最小路径可重复覆盖,因为题目中有说,如果一个点沿着某一路径走下去可以到达另一个点,则这两个点也是互相可以望见的,也就是说对于边a->b,b->c,间接的a->c也算。所以就先floyd求一下传递闭包,建立拆点二分图,然后跑一遍DAG最小路径点覆盖。(这类问题博主更习惯于直接用邻接矩阵QAQ)

 

/**
 *
 * Author: correct
 *
 */
#include <bits/stdc++.h>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 210;
bool w[N][N];
int match[N];
bool vis[N];
int n, m;
bool dfs(int x){
	for (int i = 1; i <= n; i++){
		if (w[x][i] && !vis[i]){
			vis[i] = 1;
			if (match[i] == -1 || dfs(match[i])){
				match[i] = x;
				return 1;
			}
		}
	}
	return 0;
}
void pre_work(){
	mem(match, -1);
	mem(vis, 0);
	for (int k = 1; k <= n; k++){
		for (int i = 1; i <= n; i++){
			for (int j = 1; j <= n; j++){
				w[i][j] = (w[i][k] && w[k][j]) || w[i][j];
			}
		}
	}
}
int solve(){
	int ans = n;
	for (int i = 1; i <= n; i++){
		mem(vis, 0);
		if (dfs(i))ans--;
	}
	return ans;
}
int main()
{
	mem(w, 0);
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++){
		int x, y;
		scanf("%d %d", &x, &y);
		w[x][y] = 1;
	}
	pre_work();
	printf("%d\n", solve());
	return 0;
}

 

发布了204 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/104731791