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
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 = ;
int Map[N][N],check[N*],match[N*],Link[N][N*];
int n,m,ans; inline int dfs(int x){
for(int i=n+;i<=*n;i++){
if(Link[x][i] && !check[i]){
check[i]=;
if(!match[i] || dfs(match[i])){
match[i]=x;
return ;
}
}
}
return ;
} int main(){
scanf("%d%d",&n,&m);
ans=;
for(int i=,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
Map[x][y]=;
}
for(int k=;k<=n;k++){
for(int i=;i<=n;i++){
if(Map[i][k])
for(int j=;j<=n;j++){
if(Map[k][j]) Map[i][j]=;
}
}
}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
if(Map[i][j]) Link[i][j+n]=;
for(int i=;i<=n;i++){
memset(check,,sizeof(check));
if(dfs(i)) ans++;
}
printf("%d\n",n-ans);
return ;
}