[BZOJ 1143] 祭祀river

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1143

 

Solution:

一道最长反链的模板题

 

由Dilworth定理可知:最小链覆盖数(偏序集能划分成的最少的全序集的个数) = 最长反链长度

其对偶定理:最长链长度 = 最小反链覆盖数

(VFleaking的证明:http://vfleaking.blog.163.com/blog/static/1748076342012918105514527/)

 

求解最小链覆盖的方式:

1、先用Floyd求出传递闭包,表示哪些(x,y)间是可以相互抵达的

2、将每个点拆分,最小链覆盖=n-二分图最大匹配

 

证明:每匹配两个点,则意味着少了一条链,从而最少链的数量为n-最大匹配的数量

而先计算传递闭包是为了实现路径的可交叉,相当于“跳过”交叉点进行匹配

 

Code:

#include <bits/stdc++.h>

using namespace std;

const int MAXN=105;
int n,m,x,y,match[MAXN*2],f[MAXN][MAXN],vis[MAXN*2];

bool dfs(int x)
{
    for(int i=1;i<=n;i++)
        if(f[x][i] && !vis[i])
        {
            vis[i]=true;
            if(match[i]==-1 || dfs(match[i]))
            {
                match[i]=x;
                return true;
            }
        }
    return false;
}

int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;i++)
        cin >> x >> y,f[x][y]=1;
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                f[i][j]|=f[i][k]&&f[k][j]&&!(i==j);
    
    memset(match,-1,sizeof(match));
    int res=n;
    for(int i=1;i<=n;i++)
        memset(vis,0,sizeof(vis)),res-=dfs(i);
    for(int i=1;i<=n;i++) cout << match[i] << " ";
    cout << endl;
    cout << res;
    return 0;
}

 

Review:

1、最小链覆盖(交叉/不交叉)的求法

2、记住将i=j的f[i][j]设为0

 

posted @ 2018-05-27 09:00  NewErA  阅读(101)  评论(0编辑  收藏