[BZOJ1143][CTSC2008]祭祀river 做题笔记

题目来源:http://www.lydsy.com/JudgeOnline/problem.php?id=1143
其实说是原创,也就是代码而已。。。
一种理解,二分图的最大独立集(引用自http://www.cnblogs.com/BLADEVIL/p/3713043.html):

  我们可以将一个点拆成两个点x,y,那么如果存在一条i->j的路径,我们就连接xi,yj,那么答案就是n-最大匹配数。

  因为i->j所以对于i与j只能选一个,那么我们只需要求出来二分图的最大独立集就可以了,二分图的最大独立集=点数-最大匹配。

另一种思路是最长反链(引用自http://www.bubuko.com/infodetail-664202.html,不知道原作者是谁真是太抱歉了)。

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

链:一条链是一些点的集合,链上任意两个点x, y,满足要么 x 能到达 y ,要么 y 能到达 x 。

反链:一条反链是一些点的集合,链上任意两个点x, y,满足 x 不能到达 y,且 y 也不能到达 x。

那么很显然这道题就是求最长反链长度了。

一个定理:最长反链长度 = 最小链覆盖(用最少的链覆盖所有顶点)

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

那么我们要求出的就是这个有向无环图的最小链覆盖了。最小链覆盖也就是路径可以相交的最小路径覆盖。

我们先来看路径不能相交的最小路径覆盖怎么来做:

建立一个二分图,两边都是n个点,原图的每个点 i 对应两个,在左边的叫做 i1, 在右边的叫做 i2 。

然后原图中如果存在一条边 (x, y),那么就在二分图中建立 (x1, y2) 的边。

这样建立二分图之后,原图的点数 n - 二分图最大匹配 = 原图的最小路径覆盖(路径不能相交)。

这样为什么是对的呢?我们可以认为,开始时原图的每个点都是独立的一条路径,然后我们每次在二分图中选出一条边,就是将两条路径连接成一条路径,答案数就减少1。

而路径是不能相交的,所以我们在二分图中选出的边也是不能相交的,所以就是二分图的最大匹配。

了解了路径不能相交的最小路径覆盖之后,怎么解路径可以相交的最小路径覆盖(也就是最小链覆盖)呢?

我们将原图做一次Floyd传递闭包,之后就可以知道任意两点 x, y,x 是否能到达 y。

如果两个点 x, y,满足 x 可以到达 y ,那么就在二分图中建立边 (x1, y2) 。

这样其实就是相当于将原图改造了一下,只要 x 能到达 y ,就直接连一条边 (x,
y),这样就可以“绕过”原图的一些被其他路径占用的点,直接构造新路径了。

这样就将可以相交的最小路径覆盖转化为了路径不能相交的最小路径覆盖了。


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int inf=0x3fffffff;
int map[205][205];
int maxflow=0,s,t,n,m,tot=1;
int d[400];
int head[400],e[160000],nxt[160000],ver[160000];
void add (int u,int v,int w) {
    ver[++tot]=v;e[tot]=w;nxt[tot]=head[u];head[u]=tot;
    ver[++tot]=u;e[tot]=0;nxt[tot]=head[v];head[v]=tot;
}
void floyd () {
    int i,j,k;
    for (k=1;k<=n;k++)
        for (i=1;i<=n;i++) 
            for (j=1;j<=n;j++) 
                map[i][j]|=(map[i][k]&map[k][j]);
}
bool bfs () {
    queue <int> q;
    memset(d,0,sizeof(d));
    q.push(s); d[s]=1;
    while (!q.empty()) {
        int x=q.front(); q.pop();
        for (int i=head[x];i;i=nxt[i])
            if (e[i]&&!d[ver[i]]) {
                q.push(ver[i]);
                d[ver[i]]=d[x]+1;
                if (ver[i]==t) return 1;
            }
    }
    return 0;
}
int dinic (int x,int f) {
    int rest=f;
    if (x==t) return f;
    for (int i=head[x];i&&rest;i=nxt[i]) 
        if (e[i]&&d[ver[i]]==d[x]+1) {
            int now=dinic(ver[i],min(e[i],rest));
            if (!now) d[ver[i]]=0;
            e[i]-=now;
            e[i^1]+=now;
            rest-=now;
        }
    return f-rest;
}
void build () {
    for (int i=1;i<=n;i++) 
        add(s,i,1),add(i+n,t,1);
    for (int i=1;i<=n;i++) 
        for (int j=1;j<=n;j++) 
            if (map[i][j]) add(i,j+n,inf);
}
int main () {
    int tmp,u,v;
    scanf("%d%d",&n,&m);
    s=0,t=2*n+1;
    for (int i=1;i<=m;i++) {
        scanf("%d%d",&u,&v);
        map[u][v]=1;
    }
    floyd();
    build();
    while (bfs()) 
        while (tmp=dinic(s,inf)) maxflow+=tmp;
    printf("%d\n",n-maxflow);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章