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
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; }
重要的是自信,一旦有了自信,人就会赢得一切。