1. 程式人生 > >[BZOJ 1143] 祭祀river

[BZOJ 1143] 祭祀river

col ++ code ive 匹配 最大 長度 AI ron

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

[BZOJ 1143] 祭祀river