CH6902 Vani和Cl2捉迷藏

描述

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)

01 #include <iostream>
02 #include <cstdio>
03 #include <cstring>
04 #include <algorithm>
05 #include <vector>
06 using namespace std;
07 //一些定义
08 const int ACCEPT = 0;
09 const int WRONG_ANSWER = 1;
10 //fstd 标准输出 fout 选手输出 fin 标准输入
11 FILE *fstd,*fout,*fin;
12 int n, m, ans, val;
13 bool v[210], f[210];
14 vector<int> ver[210];
15  
16 bool dfs(int x) {
17     f[x] = 1;
18     if (v[x]) return true;
19     for (int i = 0; i < ver[x].size(); i++) {
20         int y = ver[x][i];
21         if (f[y]) continue;
22         if (dfs(y)) return true;
23     }
24     return false;
25 }
26  
27 //执行比较操作。
28 bool DoCompare(){
29     fscanf(fin, "%d%d", &n, &m);
30     for (int i = 1; i <= m; i++) {
31         int x, y;
32         fscanf(fin, "%d%d", &x, &y);
33         ver[x].push_back(y);
34     }
35     fscanf(fstd, "%d", &ans);
36     fscanf(fout, "%d", &val);
37     // 答案不对
38     if (val != ans) return false;
39     for (int i = 1; i <= ans; i++) {
40         int x; fscanf(fout, "%d", &x);
41         // 输出的藏身点不合法或有重复
42         if (x < 1 || x > n || v[x]) return false;
43         v[x] = 1;
44     }
45     for (int i = 1; i <= n; i++) {
46         if (!v[i]) continue;
47         memset(f, 0, sizeof(f));
48         v[i] = 0;
49         // 能看到别的点
50         if (dfs(i)) return false;
51         v[i] = 1;
52     }
53     return true;
54 }
55  
56 int main(int argc, char* argv[])
57 {
58     if(argc!=4){
59         printf("参数不足 %d",argc);
60         return -1;
61     }
62  
63     //打开文件
64     if(NULL==(fstd=fopen(argv[1],"r"))){
65         return -1;
66     }
67     if(NULL==(fout=fopen(argv[2],"r"))){
68         return -1;
69     }
70     if(NULL==(fin=fopen(argv[3],"r"))){
71         return -1;
72     }
73  
74     if(DoCompare()){
75         return ACCEPT;
76     }else{
77         return WRONG_ANSWER;
78     }
79 }

题解

藏身点个数等于最小路径可重复点覆盖包含的路径条数。只需传递闭包,拆点跑二分图最大匹配,用点数减去它就行了。

证明见《进阶》,是一个利用了反证法的构造。

#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;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10978966.html
今日推荐