【bzoj1143】【CTSC2008】【祭祀】【river】

来源:互联网 发布:java接口开发demo下载 编辑:程序博客网 时间:2024/06/09 20:12

题目大意

给出一幅有向图,选取一些点,这些点不能从一个点到达另一个点,求最多能选多少个点。

题解

其实我们用floyd传递闭包,知道哪些点不能同时取。显然就是求最大独立集,对偶问题是最小点覆盖,等价于二分图最大匹配,建个图后跑一遍网络流就可以了。

最大独立集对偶问题是最小点覆盖

设属于独立集与属于覆盖集的共同构成整个图,由覆盖集的定义,一条边至少有一个点属于覆盖集,也就是没有一条边两个点都属于独立集,所以得证属于独立集与属于覆盖集的共同构成整个图。所以当覆盖集最小时,独立集最大。

最小点覆盖等价于二分图最大匹配

割边代表选取了某一个点并付出代价,最小割使得一条边至少一个点被选中,满足了覆盖集的要求,而最小割就最小化选的点,即最小点覆盖。

ps:以上证明比较感性

code

#include<set>#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,j,k) for(int i=j;i<=k;i++)#define fd(i,j,k) for(int i=j;i>=k;i--)using namespace std;int const maxn=200,maxm=30000,inf=2147483647;int n,m,cntedge=2,begin[maxn*2+10],to[maxn*maxn*2+maxn*4+10],size[maxn*maxn*2+maxn*4+10],    cost[maxn*maxn*2+maxn*4+10],next[maxn*maxn*2+maxn*4+10],dis[maxn*2+10];bool done[maxn*2+10],tong[maxn+10][maxn+10];void insert(int u,int v,int w,int c){    to[cntedge]=v;    size[cntedge]=w;    cost[cntedge]=c;    next[cntedge]=begin[u];    begin[u]=cntedge++;    to[cntedge]=u;    size[cntedge]=0;    cost[cntedge]=-c;    next[cntedge]=begin[v];    begin[v]=cntedge++;}int addflow(int now,int t,int flow){    done[now]=1;    if(now==t)return flow;    for(int i=begin[now];i;i=next[i])        if(size[i]&&(!done[to[i]])&&(dis[now]==dis[to[i]]+cost[i])){            int add=addflow(to[i],t,min(flow,size[i]));            if(add){                size[i]-=add;size[i^1]+=add;                return add;            }        }    return 0;}bool updis(){    int add=inf;    fo(i,0,n*2+1)        if(done[i])            for(int j=begin[i];j;j=next[j])                if(size[j]&&(!done[to[j]]))                    add=min(add,dis[to[j]]+cost[j]-dis[i]);    if(add==inf)return 0;    fo(i,0,n*2+1)        if(done[i])dis[i]+=add;    return 1;}int main(){    freopen("d.in","r",stdin);    freopen("d.out","w",stdout);    scanf("%d%d",&n,&m);    fo(i,1,m){        int u,v;scanf("%d%d",&u,&v);        tong[u][v]=1;    }    fo(k,1,n)        fo(i,1,n)            fo(j,1,n)                tong[i][j]|=tong[i][k]&&tong[k][j];    fo(i,1,n)        fo(j,1,n)            if(tong[i][j])insert(i,n+j,1,1);    fo(i,1,n)insert(0,i,1,1),insert(n+i,n*2+1,1,1);    int ans=0;    do{        memset(done,0,sizeof(done));int add;        while(add=addflow(0,n*2+1,inf)){            ans+=add;            memset(done,0,sizeof(done));        }    }while(updis());    printf("%d",n-ans);    return 0;}
0 0
原创粉丝点击