BZOJ 1143 [CTSC2008]祭祀river

来源:互联网 发布:网络传输异常 编辑:程序博客网 时间:2024/06/09 19:41

传递闭包+二分图最大独立集。

如果i能到达k,k能到达j,那么i能到达j,连边。

因为最终答案的点之间肯定不存在边,所以跑二分图最大匹配,计算最大独立集即可。

顺便学一些二分图的知识,本题考察下文第六点

以下资料来自这里,感谢原博主。我对它进行了一些整理修改和补充。


注意,以下的图中不应存在一个点,与图中的其他点都没有连边。

常用定理:
最小点覆盖=最大匹配。
最小边覆盖=最大独立集=图中点的个数-最大匹配。
最小路径覆盖=原图的节点数-新图最大匹配。(详见五、六)

一、二分图最大匹配
·定义:匹配是图中一些边的集合,且集合中任意两条边都没有公共点,所有的匹配中,边数最多的就是最大匹配。
·算法:用匈牙利算法可以在O(V*E)的复杂度内求出二分图的最大匹配。

二、二分图最小点覆盖
· 定义:点覆盖是图中一些点的集合,且对于图中所有的边,至少有一个端点属于点覆盖,点数最小的覆盖就是最小点覆盖。
·定理:最小点覆盖=最大匹配。
·简单证明:首先必然有,最小覆盖≥最大匹配。于是只要证明不等式可以取等号,我们可在最大匹配的基础上构造出一组点覆盖。对右边每一个未匹配的点进行dfs找增广路,标记所有dfs过程中访问到的点,左边标记的点+右边未标记的点就是这个图的一个点覆盖。因为对于任意一条边,如果他的左边没标记,右边被标记了,那么我们就可找到一条新的增广路,所以每一条边都至少被一个点覆盖。再来证明:最大匹配=左边标记的点+右边未标记的点。对于每条匹配边,只有一个点属于点覆盖。如果这条边在dfs过程中被访问了,那么就左端点属于点覆盖,右端点不属于,否则就有左端点不属于点覆盖,右端点属于点覆盖。除此之外,不可能存在其它的点属于最小覆盖了,不然就必然可以找到增广路。所以:左边标记的点+右边未标记的点=最大匹配,对于任意的二分图,我们总能在最大匹配的基础上构造出一组点数等于最大匹配的点覆盖,所以:最小点覆盖=最大匹配。

三、二分图最小边覆盖
· 定义:边覆盖是图中一些边的集合,且对于图中所有的点,至少有一条集合中的边与其相关联,边数最小的覆盖就是最小边覆盖。
·定理:最小边覆盖=图中点的个数-最大匹配。
·简单证明:先贪心的选一组最大匹配的边加入集合,对于剩下的每个未匹配的点,随便选一条与之关联的边加入集合,得到的集合就是最小边覆盖,所以有:最小边覆盖=最大匹配+图中点的个数-2*最大匹配=图中点的个数-最大匹配。

四、二分图最大独立集
· 定义:独立集是图中一些点的集合,且图中任意两点之间都存在边,点数最大的就是最大独立集。
·定理:最大独立集=图中点的个数-最大匹配。
·简单证明:可以这样来理解,先把所有的点都加入集合,删除最少的点和与其关联的边使得剩下的点相互之间不存在边,我们就得到了最大独立集。所以有:最大独立集=图中点的个数-最小点覆盖=图中点的个数-最大匹配。

五、有向无环图最小不相交路径覆盖
· 定义:用最少的不相交路径覆盖所有顶点。
·定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax->By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。
·简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。

六、有向无环图最小可相交路径覆盖
·定义:用最小的可相交路径覆盖所有顶点。
·算法:先用floyd求出原图的传递闭包,即如果a到b有路,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。
·简单证明:为了连通两个点,某条路径可能经过其它路径的中间点。但是如果两个点a和b是连通的,只不过中间需要经过其它的点,那么可以在这两个点之间加边,那么a就可以直达b,不必经过中点的(实现了“跳跃”),那么就转化成了最小不相交路径覆盖。

七、偏序集的最大反链
· 定义:偏序集中的最大独立集。
· Dilworth定理:对于任意偏序集都有,最大独立集(最大反链)=最小链的划分(最小可相交路径覆盖)。
·通过Dilworth定理,我们就可以把偏序集的最大独立集问题转化为最小不相交路径覆盖问题了。

八、二分图带权最大匹配
· 定义:每个边都有一组权值,边权之和最大的匹配就是带权最大匹配。
·算法:KM算法,复杂度为O(V^3)。具体就不说了,网上有不少资料。
·要注意的是,KM算法求的是最佳匹配,即在匹配是完备的基础上权值之和最大。这和带权最大匹配是不一样的,不过我们可以加入若干条边权为0的边使得KM求出来的最佳匹配等于最大权匹配。具体实现的时候最好用矩阵来存图,因为一般点的个数都是10^2级别,并且这样默认任意两点之间都存在边权为0的边,写起来很方便。如果要求最小权匹配,我们可以用一个很大数减去每条边的边权。


然后接下来是这题的代码- -

#include<cstdio>#include<cstring>using namespace std;bool vis[105], d[105][105];int mat[105], n, m;bool find(int x){    for(int i = 1; i <= n; i++)    {        if(d[x][i] && !vis[i])        {            vis[i]=1;            if(!mat[i] || find(mat[i]))            {                mat[i]=x;                return 1;            }        }    }    return 0;}int main(){    scanf("%d%d",&n,&m);    for(int i = 1; i <= m; i++)    {        int x, y;        scanf("%d%d",&x,&y);        d[x][y]=1;    }    for(int k = 1; k <= n; k++)        for(int i = 1; i <= n; i++)            for(int j = 1; j <= n; j++)                d[i][j]|=(d[i][k]&d[k][j]);    int ans=0;    for(int i = 1; i <= n; i++)    {        memset(vis,0,sizeof(vis));        if(find(i))ans++;    }    printf("%d\n",n-ans);} 
1 0
原创粉丝点击