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

题目大意

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

题解

其实我们用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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值