【bzoj1143: [CTSC2008]祭祀river】有向无环图的最长反链

来源:互联网 发布:重装系统安装不了软件 编辑:程序博客网 时间:2024/04/29 06:06

1143: [CTSC2008]祭祀river

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3192  Solved: 1632
[Submit][Status][Discuss]

Description

  在遥远的东方,有一个神秘的民族,自称Y族。他们世代居住在水面上,奉龙王为神。每逢重大庆典, Y族都
会在水面上举办盛大的祭祀活动。我们可以把Y族居住地水系看成一个由岔口和河道组成的网络。每条河道连接着
两个岔口,并且水在河道内按照一个固定的方向流动。显然,水系中不会有环流(下图描述一个环流的例子)。

 

  由于人数众多的原因,Y族的祭祀活动会在多个岔口上同时举行。出于对龙王的尊重,这些祭祀地点的选择必
须非常慎重。准确地说,Y族人认为,如果水流可以从一个祭祀点流到另外一个祭祀点,那么祭祀就会失去它神圣
的意义。族长希望在保持祭祀神圣性的基础上,选择尽可能多的祭祀的地点。

Input

  第一行包含两个用空格隔开的整数N、M,分别表示岔口和河道的数目,岔口从1到N编号。接下来M行,每行包

含两个用空格隔开的整数u、v,描述一条连接岔口u和岔口v的河道,水流方向为自u向v。 N ≤ 100 M ≤ 1 000

Output

  第一行包含一个整数K,表示最多能选取的祭祀点的个数。

Sample Input

4 4
1 2
3 4
3 2
4 2

Sample Output

2

【样例说明】
在样例给出的水系中,不存在一种方法能够选择三个或者三个以上的祭祀点。包含两个祭祀点的测试点的方案有两种:
选择岔口1与岔口3(如样例输出第二行),选择岔口1与岔口4。
水流可以从任意岔口流至岔口2。如果在岔口2建立祭祀点,那么任意其他岔口都不能建立祭祀点
但是在最优的一种祭祀点的选取方案中我们可以建立两个祭祀点,所以岔口2不能建立祭祀点。对于其他岔口
至少存在一个最优方案选择该岔口为祭祀点,所以输出为1011。


icpc上居然出了这个原题,就爆炸了,回来补一补二分图和有向无环图的东西。


1、二分图最大匹配

匈牙利算法O(V*E)的复杂度内可求出二分图的最大匹配

2、二分图最小点覆盖

找到点数最少的一个点集,使得二分图里的所有边至少有一个端点是在该点集中

最小点覆盖=最大匹配

3、二分图最小边覆盖

找到边数最少的一个边集,使得二分图里的所有点至少是一条边的端点

最小边覆盖=图中点的个数-最大匹配

证明:把最大匹配的边都加入,则剩下的点之间都没有边,那么再把剩下的点都连接起来,边数=最大匹配+图中点的个数-2*最大匹配=图中点的个数-最大匹配

4、二分图最大独立集

找到一个点数最大的点集,使得点集里的所有点对之间在原图里没有边。

最大独立集=图中点的个数-最大匹配

证明:把所有点加入点集中,最大独立集问题就转化为删去最少的点使得最终的点集是个独立集,那么 最大独立集=图中点的个数-最小点覆盖=图中点的个数-最大匹配

最小不相交路径覆盖:每一条路径经过的顶点各不相同。如,最小路径覆盖数为31->3>425

最小可相交路径覆盖:每一条路径经过的顶点可以相同。如,最小路径覆盖数为21->3->42->3>5

5、有向无环图最小不相交路径覆盖

用最少的不相交路径覆盖所有顶点。

将有向无环图中的各点拆点,i点拆为ai,bi  ,对于一条边:i->j,转化为 ai->bj, 便可把有向无环图转化为二分图。

有向无环图最小不相交路径覆盖=图中点的个数-最大匹配

证明:刚开始每个点为一条路径,则开始时有n条路径,对于二分图中的每一条匹配边就代表把两条路径变为一条路径,则最后的答案就是图中点的个数-最大匹配

6、有向无环图最小可相交路径覆盖

用最少的可相交路径覆盖所有顶点。

处理出有向无环图中所有点的到达情况,拆点,若i点可经过一条路径到达j点,那么连一条ai->bj 的边,此时有向图边转化为二分图,问题也就转化为5、有向无环图最小不相交路径覆盖。

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

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

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

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

对偶定理:最长链长度 =最小反链覆盖 ///????这个是什么意思没看懂,有人看懂了评论下

最小链覆盖也就是最小可相交路径覆盖。


经过上面一些乱七八糟的介绍,可以知道,这道题目就是求最长反链长度,就是求最长反链长度,就是求有向无环图最小可相交路径覆盖,见(6)

求各点的联通性可以用Floyd 也可以直接搜。

#include<cstdio>#include<cstring>#define M 1005#define N 105using namespace std;int k,K,fir[N],Fir[N],n,m,l,r,sign[N],link[N],vis[N];struct he{    int r,nx;}A[N*N],a[M];void add(int l,int r){    k++;a[k].r=r;a[k].nx=fir[l];fir[l]=k;}void Add(int l,int r){    K++;A[K].r=r;A[K].nx=Fir[l];Fir[l]=K;}void dfs(int x,int f){    vis[x]=1;    if(x!=f)Add(f,x);    for(int i=fir[x];i!=-1;i=a[i].nx)    if(!vis[a[i].r]){        dfs(a[i].r,f);    }}bool check(int x){    for(int i=Fir[x];i!=-1;i=A[i].nx)    if(!sign[A[i].r]){        sign[A[i].r]=1;        if(link[A[i].r]==0){            link[A[i].r]=x;            return true;        }        if(check(link[A[i].r])){            link[A[i].r]=x;            return true;        }    }    return false;}int hungery(){    int ans=n;    for(int i=1;i<=n;i++){        memset(sign,0,sizeof(sign));        if(check(i)) ans--;    }    return ans;}int main(){    memset(fir,-1,sizeof(fir));    memset(Fir,-1,sizeof(Fir));    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++){        scanf("%d%d",&l,&r);        add(l,r);    }    for(int i=1;i<=n;i++){        memset(vis,0,sizeof(vis));        dfs(i,i);    }    printf("%d",hungery());}



阅读全文
0 0
原创粉丝点击