【网络流】hdu3313 Key Vertex

题意:给定一个有向图和源汇,求图中割点的个数
难度:4.5
题解:这题比较神,做法是将每个点拆点,然后中间连边流量为1,图中的边连边流量为inf,然后比较特殊的是S和T所拆得点间流量为2。然后这样的话,如果流量为2,那么图中除了S和T没有割点。如果流量为1,那么增广路上的有可能是割点,然后利用求最小割的dfs算法求出各个割。如果流量为0的话,那么说明S和T本来就不连通,所以任何一个点都是割点。
#include <stdio.h>
#include <string.h>
const int N=222222;
const int inf=(0x7FFFFFFF-10)/2;
struct gtp{int x,y,c,op,next;}g[1000000];
int n,m,e,S,T,ls[N],fa[N],v[N],list[N],ans,zgl[N],tt;

void addedge(int x,int y,int c){
    e++;
    g[e].x=x; g[e].y=y; g[e].c=c; g[e].op=e+1;
    g[e].next=ls[x]; ls[x]=e;
    e++;
    g[e].x=y; g[e].y=x; g[e].c=0; g[e].op=e-1;
    g[e].next=ls[y]; ls[y]=e;
}   

void init(){
    e=0;
    memset(ls,0,sizeof(ls));
    memset(zgl,0,sizeof(zgl));
    for (int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x+n,y,inf);
    }
    scanf("%d%d",&S,&T);
    for (int i=0;i<n;i++)
      if (i!=S && i!=T) addedge(i,i+n,1);
                   else addedge(i,i+n,2);
    T+=n;
}

bool bfs(){
    for (int i=0;i<2*n;i++) v[i]=0;
    int head=0,tail=1;
    list[1]=S;
    while (head!=tail){
        head++;
        for (int t=ls[list[head]];t;t=g[t].next)
          if (!v[g[t].y] && g[t].c){
              v[g[t].y]=1;
              tail++;
              list[tail]=g[t].y;
              fa[g[t].y]=t;
          }
    }
    if (v[T]) return true;
    return false;
}   

void change(){
    for (int i=T;i!=S;i=g[fa[i]].x){
        g[fa[i]].c--;
        g[g[fa[i]].op].c++;
        zgl[i]=1;
    }   
    zgl[S]=1;
}   

int ek(){
    bool flag=bfs();
    if (!bfs()) return 0;
    change();
    flag=bfs();
    if (!bfs()) return 1;
    return 2;
}   

void dfs(int k){
    tt++;
    list[tt]=k;
    v[k]=1;
    for (int t=ls[k];t;t=g[t].next)
      if (g[t].c && !v[g[t].y]) dfs(g[t].y);
}   

void work(){
    g[ls[S]].c=0;
    g[ls[T-n]].c=0;
    memset(v,0,sizeof(v));
    ans=0;
    int st=S;
    for (;;){
        tt=0;
        dfs(st);
        bool flag=false;
        for (int i=1;i<=tt;i++) if (!flag)
          for (int t=ls[list[i]];t;t=g[t].next)
            if ((t&1) && !g[t].c && !v[g[t].y]){
                ans++;
                flag=true;
                st=g[t].y;
                if (g[t].y==T) return;
            }   
    }   
}   

int main(){
    while (scanf("%d%d",&n,&m)!=EOF){
        init();
        if (S+n==T){
            printf("1\n");
            continue;
        }   
        int flow=ek();
        if (flow==0) printf("%d\n",n);
        else
        if (flow==2) printf("2\n");
        else{
            work();
            printf("%d\n",ans);
        }   
    }   
}   


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值