AcWing 379. 捉迷藏
原题链接
考察:二分图
思路:
求最大的点集合,集合内任意两点不可以抵达.求的答案略像最小路径重复点覆盖,每条路径上一定存在其他路径无法抵达的点.我们假设答案为\(ans\),最小路径重复点覆盖条数为\(cnt\).
明显选了一条路径上的点就不能再选这条路径的其他点.所以\(ans>=cnt\)
假设每条路径的终点集合为\(E\),那么\(E\)可以到达的点集合为\(ne(E)\).接下来有两个情况:
(1) \(E与ne(E)\)没有交点.由此答案就可以取\(cnt\).
(2) \(存在交点p\),那么我们让p往回走,直到没有交点为止.可以发现一定可以找到没有交点的位置,否则违背最小路径重复点覆盖的定义.
Code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 210,M = 30010;
int h[N<<1],idx,n,m,match[N<<1];
bool g[N][N],st[N<<1];
struct Road{
int to,ne;
}road[M];
void add(int a,int b)
{
road[idx].to = b,road[idx].ne = h[a],h[a] = idx++;
}
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j] = max(g[i][k]&&g[k][j],g[i][j]);
}
bool dfs(int u)
{
for(int i=h[u];~i;i=road[i].ne)
{
int v = road[i].to;
if(st[v]) continue;
st[v] = 1;
if(!match[v]||dfs(match[v]))
{
match[v] = u;
return 1;
}
}
return 0;
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
g[a][b] =1;
}
floyd();//求floyd后的最小路径覆盖
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(g[i][j])
add(i,j);
int res = 0;
for(int i=1;i<=n;i++)
{
memset(st,0,sizeof st);
if(dfs(i)) ++res;
}
printf("%d\n",n-res);
return 0;
}