1143: [CTSC2008]祭祀river(最长反链)

1143: [CTSC2008]祭祀river

题目链接https://www.lydsy.com/JudgeOnline/problem.php?id=1143

Description:

  在遥远的东方,有一个神秘的民族,自称Y族。他们世代居住在水面上,奉龙王为神。每逢重大庆典, Y族都会在水面上举办盛大的祭祀活动。我们可以把Y族居住地水系看成一个由岔口和河道组成的网络。每条河道连接着两个岔口,并且水在河道内按照一个固定的方向流动。显然,水系中不会有环流(下图描述一个环流的例子)。

 

  由于人数众多的原因,Y族的祭祀活动会在多个岔口上同时举行。出于对龙王的尊重,这些祭祀地点的选择必
须非常慎重。准确地说,Y族人认为,如果水流可以从一个祭祀点流到另外一个祭祀点,那么祭祀就会失去它神圣
的意义。族长希望在保持祭祀神圣性的基础上,选择尽可能多的祭祀的地点。
 
Input:
第一行包含两个用空格隔开的整数N、M,分别表示岔口和河道的数目,岔口从1到N编号。接下来M行,每行包含两个用空格隔开的整数u、v,描述一条连接岔口u和岔口v的河道,水流方向为自u向v。
N≤100M≤1000
 
Output:
第一行包含一个整数K,表示最多能选取的祭祀点的个数。
 
Sample Input:
4 4
1 2
3 4
3 2
4 2
Sample Output:
2
 
题解:
最长反链模板题。
链是一个点的集合,一条链上的任意两点u,v,要么u可以到v,要么v可以到u;
反链也是一个点的集合,但反链上的点是孤立的,不存在一对点u,v,满足在链上的情况。
然后有个定理就是最长反链=最小链覆盖。
这里最小链覆盖可以看作可以有交叉点的最小路径覆盖,然后就直接求就好了~具体方法是先floyd传递闭包,然后跑二分图最大匹配,n-最大匹配数  即为答案。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
 
const int N = 205 ;
int Map[N][N],check[N*2],match[N*2],Link[N][N*2];
int n,m,ans;
 
inline int dfs(int x){
    for(int i=n+1;i<=2*n;i++){
        if(Link[x][i] && !check[i]){
            check[i]=1;
            if(!match[i] || dfs(match[i])){
                match[i]=x;
                return 1;
            }
        }
    }
    return 0;
}
 
int main(){
    scanf("%d%d",&n,&m);
    ans=0;
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        Map[x][y]=1;
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            if(Map[i][k])
            for(int j=1;j<=n;j++){
                if(Map[k][j]) Map[i][j]=1;
            }
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(Map[i][j]) Link[i][j+n]=1;
    for(int i=1;i<=n;i++){
        memset(check,0,sizeof(check));
        if(dfs(i)) ans++;
    }
    printf("%d\n",n-ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/heyuhhh/p/10398529.html