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;
}
posted @ 2021-07-14 15:57  acmloser  阅读(23)  评论(0编辑  收藏  举报