偏序集的最大反链【二分图】
在有向无环图中,有如下的一些定义和性质:
链:一条链是一些点的集合,链上任意两个点x, y,满足要么 x 能到达 y ,要么 y 能到达 x 。
反链:一条反链是一些点的集合,链上任意两个点x, y,满足 x 不能到达 y,且 y 也不能到达 x。
又有诸如以下定理:
一、有向无环图最小不相交路径覆盖
定义:用最少的不相交路径覆盖所有顶点。
定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。
简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。
二、有向无环图最小可相交路径覆盖
定义:用最小的可相交路径覆盖所有顶点。
算法:先用floyd求出原图的传递闭包,即如果a到b有路,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。
三、偏序集的最大反链
定义:偏序集中的最大独立集。
Dilworth定理:对于任意偏序集都有,最大独立集(最大反链)=最小链的划分(最小可相交路径覆盖).
通过Dilworth定理, 我们就可以把偏序集的最大独立集问题转化为最小可相交路径覆盖问题了。
经典例题:
BZOJ 1143祭祀river
#include<bits/stdc++.h> using namespace std; const int MAXN=105; int g[MAXN][MAXN]; int uN,vN; int linker[MAXN]; bool used[MAXN]; bool dfs(int u) { for(int v = 0; v < vN; v++) if(g[u][v] && !used[v]) { used[v] = true; if(linker[v] == -1 || dfs(linker[v])) { linker[v] = u; return true; } } return false; } int hungary() { int res = 0; memset(linker,-1,sizeof(linker)); for(int u = 0; u < uN; u++) { memset(used,false,sizeof(used)); if(dfs(u))res++; } return res; } int main() { int n,m; while (~scanf("%d%d",&n,&m)) { memset(g,0,sizeof(g)); for (int i=1; i<=m; i++) { int u,v; scanf("%d%d",&u,&v); g[u-1][v-1]=1; } for (int k=0; k<n; k++) for (int i=0; i<n; i++) for (int j=0; j<n; j++) if (g[i][k] && g[k][j]) g[i][j]=1; uN=vN=n; printf("%d\n",n-hungary()); } return 0; }