首页 > 代码库 > [BZOJ]1143: [CTSC2008]祭祀river

[BZOJ]1143: [CTSC2008]祭祀river

题目大意:给定一个n个点m条边的有向无环图,问最多选多少个点使得两两之间互不到达。(n<=100,m<=1000)

思路:题目所求即最长反链,最长反链=最小链覆盖,对每个点向自己能到的点连边后,转化成最小路径覆盖,每个点拆成入点和出点后二分图匹配,又有最大二分图匹配=最小路径覆盖,问题得到解决。复杂度O(nm+MaxFlow(n,n^2))。

#include<cstdio>
#include<cstring>
inline int read()
{
    int x;char c;
    while((c=getchar())<0||c>9);
    for(x=c-0;(c=getchar())>=0&&c<=9;)x=(x<<3)+(x<<1)+c-0;
    return x;
}
#define MN 100
#define MM 1000
#define MV 200
#define ME 10000
#define S MV+1
#define T MV+2
#define INF 0x7FFFFFFF
namespace MaxFlow
{
    struct edge{int nx,t,w;}e[ME*2+5];
    int h[MV+5],en=1,d[MV+5],q[MV+5],qn,c[MV+5];
    inline void ins(int x,int y,int w)
    {
        e[++en]=(edge){h[x],y,w};h[x]=en;
        e[++en]=(edge){h[y],x,0};h[y]=en;
    }
    bool bfs()
    {
        int i,j;
        memset(d,0,sizeof(d));
        for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx)
            if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1;
        return d[T];
    }
    int dfs(int x,int r)
    {
        if(x==T)return r;
        int k,u=0;
        for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1)
        {
            k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w);
            u+=k;e[i].w-=k;e[i^1].w+=k;
            if(u==r)return r;
        }
        return d[x]=0,u;
    }
    int dinic(){int ans=0;while(bfs())ans+=dfs(S,INF);return ans;}
};
struct edge{int nx,t;}e[MM+5];
int h[MN+5],en,q[MN+5],qn,u[MN+5];
inline void ins(int x,int y){e[++en]=(edge){h[x],y};h[x]=en;}
int main()
{
    int n,m,i,j,k,x,y;
    n=read();m=read();
    while(m--)x=read(),y=read(),ins(x,y);
    using MaxFlow::ins;
    for(i=1;i<=n;++i)
    {
        ins(S,i,1);ins(i+n,T,1);
        memset(u,0,sizeof(u));
        for(u[q[j=qn=0]=i]=1;j<=qn;++j)for(k=h[q[j]];k;k=e[k].nx)
            if(!u[e[k].t])ins(i,e[k].t+n,INF),u[q[++qn]=e[k].t]=1;
    }
    printf("%d",n-MaxFlow::dinic());
}

 

[BZOJ]1143: [CTSC2008]祭祀river