[bzoj1143][CTSC2008]祭祀river——DAG上最长反链,Dilworth定理,最大二分图匹配,Floyd 大佬们的博客 Some Links

题目大意:

给定一个DAG图,求最长反链(即一个点集,其中任意点两两不可以相互到达)。

思路:

一开始我其实是想用求最大独立集的方法去求的。但是并不会(好像也过不去)。
题目所要求的是最长反链,需要用到一些概念和定理。
链:偏序集中任意两个元素可以相互比较(即所有的点都在DAG图的一条链上面)。
反链:偏序集中任意两个元素不可以相互比较(即所有的点都不在同一条DAG图的链上面)。
定理:最长反链等于最小链覆盖。(大概是从Dilworth定理推过来的吧)。
所以题目边转化成了求最小链覆盖的问题。想一想我们在求最小路径覆盖的时候是怎么做的,即构造二分图而求最大的匹配。
但是最小链覆盖允许不同链相交,即下面这种情况

在链覆盖中,3号点是可以同时被两条链经过的,但是在路径覆盖中,就只可以被一条路径经过。
考虑如何解决这个问题:即同一条路径可以被经过多次,但是这条路径的两头可能存在可以连通但是选不到的点,于是我们可以越过这条路径而直接选择两旁的点就好了,用Floyd搞一遍传递闭包就好了。
为什么是正确的:如果跨过某条路径的边添加到图中了,这条路径在最小链覆盖中不会被经过多次的话,就会把这条边忽略掉,反之则会被添加。

/*========================================
 * Author : ylsoi
 * Problem : bzoj1143
 * Algorithm : Dilworth and Graph Matching
 * Time : 2018.5.27
 * =====================================*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<climits>
using namespace std;
void File(){
    freopen("bzoj1143.in","r",stdin);
    freopen("bzoj1143.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=1000+10;
const int maxm=1000+10;
int n,m,b[maxn],ans;
bool dis[maxn][maxn],vis[maxn];
bool dfs(int u){
    REP(i,1,n){
        if(!dis[u][i])continue;
        if(vis[i])continue;
        vis[i]=1;
        if(!b[i] || dfs(b[i])){
            b[i]=u;
            return true;
        }
    }
    return false;
}
int main(){
    File();
    scanf("%d%d",&n,&m);
    REP(i,1,m){
        int u,v;
        scanf("%d%d",&u,&v);
        dis[u][v]=1;
    }
    REP(k,1,n)REP(i,1,n)REP(j,1,n)
        dis[i][j]|=dis[i][k]&dis[k][j];
    REP(i,1,n){
        ans+=dfs(i);
        mem(vis);
    }
    printf("%d\n",n-ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80472977
今日推荐