描述
Vani和cl2在一片树林里捉迷藏。这片树林里有N座房子,M条有向道路,组成了一张有向无环图。N≤200,M≤30000。
树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。如果从房子A沿着路走下去能够到达B,那么在A和B里的人是能够相互望见的。
现在cl2要在这N座房子里选择K座作为藏身点,同时Vani也专挑cl2作为藏身点的房子进去寻找,为了避免被Vani看见,cl2要求这K个藏身点的任意两个之间都没有路径相连。
为了让Vani更难找到自己,cl2想知道最多能选出多少个藏身点。
输入格式
输入数据的第一行是两个整数N和M。接下来M行,每行两个整数 x,y,表示一条从 x 到 y 的有向道路。
输出格式
输出一个整数K,表示最多能选取的藏身点个数。
在第二行输出 K 个空格分开的整数,表示选择的藏身点编号。如果有多个方案,输出任意一个即可。编号的输出顺序任意。
样例输入
7 5
1 2
3 2
2 4
4 5
4 6
样例输出
3
1 3 7
数据范围与约定
- 对于20% 的数据,N≤10,M<=20。
对于60% 的数据, N≤100,M<=1000。
对于100% 的数据,N≤200,M<=30000,1<=x,y<=N。
本题校验器(SPJ)
09 | const int WRONG_ANSWER = 1; |
18 | if (v[x]) return true ; |
19 | for ( int i = 0; i < ver[x].size(); i++) { |
22 | if (dfs(y)) return true ; |
29 | fscanf (fin, "%d%d" , &n, &m); |
30 | for ( int i = 1; i <= m; i++) { |
32 | fscanf (fin, "%d%d" , &x, &y); |
35 | fscanf (fstd, "%d" , &ans); |
36 | fscanf (fout, "%d" , &val); |
38 | if (val != ans) return false ; |
39 | for ( int i = 1; i <= ans; i++) { |
40 | int x; fscanf (fout, "%d" , &x); |
42 | if (x < 1 || x > n || v[x]) return false ; |
45 | for ( int i = 1; i <= n; i++) { |
47 | memset (f, 0, sizeof (f)); |
50 | if (dfs(i)) return false ; |
56 | int main( int argc, char * argv[]) |
59 | printf ( "参数不足 %d" ,argc); |
64 | if (NULL==(fstd= fopen (argv[1], "r" ))){ |
67 | if (NULL==(fout= fopen (argv[2], "r" ))){ |
70 | if (NULL==(fin= fopen (argv[3], "r" ))){ |
题解
藏身点个数等于最小路径可重复点覆盖包含的路径条数。只需传递闭包,拆点跑二分图最大匹配,用点数减去它就行了。
证明见《进阶》,是一个利用了反证法的构造。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
co int N=201;
bool cl[N][N];
int match[N],n,m;
bool vis[N],succ[N];
int hide[N];
bool dfs(int x){
for(int i=1;i<=n;++i)
if(cl[x][i]&&!vis[i]){
vis[i]=1;
if(!match[i]||dfs(match[i])){
match[i]=x;
return 1;
}
}
return 0;
}
int main(){
read(n),read(m);
while(m--) cl[read<int>()][read<int>()]=1;
for(int i=1;i<=n;++i) cl[i][i]=1;
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
cl[i][j]|=cl[i][k]&cl[k][j];
for(int i=1;i<=n;++i) cl[i][i]=0;
// Maximum Matching on Split Bipartite Graph
int ans=n;
for(int i=1;i<=n;++i){
memset(vis,0,sizeof vis);
ans-=dfs(i);
}
printf("%d\n",ans);
for(int i=1;i<=n;++i) succ[match[i]]=1;
for(int i=1,k=0;i<=n;++i)
if(!succ[i]) hide[++k]=i;
memset(vis,0,sizeof vis);
for(bool modify=1;modify;){
modify=0;
for(int i=1;i<=ans;++i)
for(int j=1;j<=n;++j)
if(cl[hide[i]][j]) vis[j]=1;
for(int i=1;i<=ans;++i)
if(vis[hide[i]]){
modify=1;
while(vis[hide[i]]) hide[i]=match[hide[i]];
}
}
for(int i=1;i<=ans;++i) printf("%d ",hide[i]);
return 0;
}
CH6902 Vani和Cl2捉迷藏
原文:https://www.cnblogs.com/autoint/p/10978966.html