1143: [CTSC2008]祭祀river
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3835 Solved: 1970
[Submit][Status][Discuss]
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
【样例说明】
在样例给出的水系中,不存在一种方法能够选择三个或者三个以上的祭祀点。包含两个祭祀点的测试点的方案有两种:
选择岔口1与岔口3(如样例输出第二行),选择岔口1与岔口4。
水流可以从任意岔口流至岔口2。如果在岔口2建立祭祀点,那么任意其他岔口都不能建立祭祀点
但是在最优的一种祭祀点的选取方案中我们可以建立两个祭祀点,所以岔口2不能建立祭祀点。对于其他岔口
至少存在一个最优方案选择该岔口为祭祀点,所以输出为1011。
HINT
Source
思路: 该题实质就是求一个最长反链长度。
链:一条链是一些点的集合,链上任意两个点x, y,满足要么 x 能到达 y ,要么 y 能到达 x 。
反链:一条反链是一些点的集合,链上任意两个点x, y,满足 x 不能到达 y,且 y 也不能到达 x。
一个定理:最长反链长度 = 最小链覆盖(用最少的链覆盖所有顶点)
对偶定理:最长链长度 = 最小反链覆盖
最小链覆盖其实就是最小可相交路径覆盖。
有向无环图最小不相交路径覆盖
把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。
简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。
有向无环图最小可相交路径覆盖
先floyd求出连通性,这样最小可相交路径覆盖就可以转化成最小不可相交路径覆盖。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N =205;
int girl[N],boy[N];
int vis[N];
int mp[N][N];
vector<int >ve[N];
int n,m;
int dfs(int u)
{
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(!vis[v]){
vis[v]=1;
if(girl[v]==-1||dfs(girl[v])){
girl[v]=u;
boy[u]=v;
return 1;
}
}
}
return 0;
}
void xiongyali()
{
int cnt=0;
memset(girl,-1,sizeof(girl));
memset(boy,-1,sizeof(boy));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i)) cnt++;
}
printf("%d\n",n-cnt);
return ;
}
int main()
{
int u,v;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d %d",&u,&v);
mp[u][v]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
if(mp[i][k]&&mp[k][j]) mp[i][j]=1;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
if(mp[i][j]) ve[i].push_back(j);
}
}
xiongyali();
return 0;
}