您的位置:首页 > 其它

BZOJ 1143 祭祀river【二分图之偏序集的最大反链】

2017-11-30 18:52 302 查看
传送门

//题意: 就是给定一个有向无环图, 选择尽量多的点使得其中任意的两个点都不能相互到达.

//思路:

在有向无环图中,有如下的一些定义和性质:

链:一条链是一些点的集合,链上任意两个点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定理, 我们就可以把偏序集的最大独立集问题转化为最小可相交路径覆盖问题了.

然后知道这些了, 这道题就变成了裸题了…..

AC Code

const int maxn = 2e2+5;
int s[maxn][maxn];
int n,m;
void floyd()   //求传递闭包.
{
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
s[i][j]=min(s[i][j],s[i][k]+s[k][j]);
}
}
}
}
bool vis[maxn];
int links[maxn];
vector<int>ve[maxn];
bool Find(int x)   //二分图匹配.
{
vis[x] = 1;
for(int i=0;i<ve[x].size();i++){
int m = ve[x][i];
if(vis[m]) continue;
vis[m] = true;
if(!links[m] || Find(links[m])){
links[m] = x;   //有向图,注意标记的方向.
return true;
}
}
return false;
}
void solve() {
scanf("%d%d" ,&n, &m);
Fill(s,inf);
for (int i = 1; i <= m ; i++) {
int u, v;
scanf("%d%d", &u, &v);
s[u][v] = 1;
}
floyd();
for (int i = 1 ; i <= n ; i++) {
for (int j = 1 ; j <= n ; j++) {
if (s[i][j] != inf) {
ve[i].pb(j + n);   //拆点,直接加一个n即可.
//千万不要忘了你建的是有向图.
}
}
}
int ans = 0; Fill(links,0);
for (int i = 1 ; i <= n * 2; i++) {
Fill(vis,0);
if(!links[i] && Find(i))
ans++;
}
cout << n - ans << endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: