http://www.lydsy.com/JudgeOnline/problem.php?id=1143
据说是南宁m题?
关键是问题的转化,然后就是一个板子。
最大反链覆盖->最小链覆盖。
链就是可以到达的点(任意两个,u可以到达v或者v可以到达u)
最小链覆盖->最小路径覆盖(可交叉)
另外。最近才发现的,二分图的求解往往是隐式拆点了。
看着没有拆,可是求解的过程 用的linker和used,是已经当成两个点处理的。
/**************************************************************
Problem: 1143
User: abcd156555
Language: C++
Result: Accepted
Time:40 ms
Memory:1444 kb
****************************************************************/
#include <bits/stdc++.h>
using namespace std;
/* 这道题是求最小链覆盖。
啥是链呢,就是一个数组里面的数,每一个都能到达另一个(可能并不能相互到达)
但是可以到达,就叫最小链覆盖。
那么这道题就是最大反链覆盖了。
根据偏序集定理,最大反链覆盖就是 最小链覆盖。
(这个是,有证明的。)
那么我们就直接求 最小链覆盖就好了
那么,如何求最小链覆盖呢。
说实话,我也不会。但是我会百度。
有人说可以最小可相交路径覆盖 就好了
因为 最小链的意思就是 这些点的数目尽可能的小,
并且他们之间的路径 可以经过所有的点。
这些最小链是可以相交的。
*/
const int maxn=200;
int mp[maxn][maxn];
int linker[maxn];
bool used[maxn];
int m,n;
bool dfs(int u){
for(int v=1;v<=m;v++){
if(mp[u][v]&&!used[v]){
used[v]=true;
if(linker[v]==-1||dfs(linker[v])){
linker[v]=u;
return true;
}
}
}
return false;
}
int main()
{ int a,b;
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d%d",&a,&b);
mp[a][b]=1;
}
for(int k=1;k<=m;k++){
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++)
if(mp[i][k]&&mp[k][j])
mp[i][j]=1;
}
}
int res=0;
memset(linker,-1,sizeof(linker));
for(int i=1;i<=m;i++){
memset(used,0,sizeof(used));
if(dfs(i))
res++;
}
printf("%d\n",m-res);//最小路径覆盖就是顶点数-匹配数
return 0;
}