UOJ Logo zgjkt的博客

博客

【poj1966】Cable TV Network

2017-02-07 13:08:00 By zgjkt

题目大意

给出一个有$n$个点$m$条边的无向图

求至少要删掉几个点,使得新图存在两个点无法联通

数据范围

$0\leqslant n\leqslant 50$


题解

图的连通度问题是指:在图中删去部分元素(点或边),使得图中指定的两个点s和t不连通(不存在从s到t的路径),求至少要删去几个元素

图的连通度分为点连通度和边连通度:

  1. 点连通度:只许删点,求至少要删掉几个点(当然,s和t不能删去,这里保证原图中至少有三个点)

  2. 边连通度:只许删边,求至少要删掉几条边

并且,有向图和无向图的连通度求法不同,因此还要分开考虑(对于混合图,只需将其中所有的无向边按照无向图的办法处理、有向边按照有向图的办法处理即可)

【1】有向图的边连通度:

以$s$为源点,$t$为汇点建立网络,原图中的每条边在网络中仍存在,容量为$1$

求该网络的最小割(也就是最大流)的值即为原图的边连通度

【2】有向图的点连通度:

需要拆点来建立网络,原图中的每个点$i$在网络中拆成$i'$与$i''$,有一条边$i',i''$,容量为$1$

$(s',s'')$和$(t',t'')$例外,容量为正无穷

原图中的每条边$(i, j)$在网络中为边$(i'',j')$,容量为正无穷

以$s'$为源点、$t''$为汇点求最大流,最大流的值即为原图的点连通度

说明:显然,容量为正无穷的边不可能通过最小割,也就是原图中的边和$s、t$两个点不能删去;若边$(i,i'')$通过最小割,则表示将原图中的点i删去

【3】无向图的边连通度:

将图中的每条边$(i, j)$拆成$(i, j)$和$(j, i)$两条边,再按照有向图的办法【1】处理

【4】无向图的点连通度:

将图中的每条边$(i, j)$拆成$(i, j)$和$(j, i)$两条边,再按照有向图的办法【2】处理


代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define goedge(i,x) for(int i=last[x];i;i=e[i].next)
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
    return x*f;
}

int n,m,S,T,ans,et;
struct edge{int to,next,c;}e[5050];
int last[105],d[105],q[105],u[5050],v[5050];
inline void addedge(int u,int v,int c){
    e[++et]=(edge){v,last[u],c};last[u]=et;
    e[++et]=(edge){u,last[v],0};last[v]=et;
}

inline void build(int s,int t){
    et=1;memset(last,0,sizeof(last));
    addedge(S,s,inf);addedge(t+n,T,inf);
    For(i,0,n-1)
        if (i!=s && i!=t) addedge(i,i+n,1);
        else addedge(i,i+n,inf);
    For(i,1,m){
        addedge(u[i]+n,v[i],inf);
        addedge(v[i]+n,u[i],inf);
    }
}

inline bool bfs(){
    memset(d,-1,sizeof(d));
    queue <int> q;q.push(S);d[S]=0;
    while (!q.empty()){
        int h=q.front();q.pop();
        goedge(i,h){
            if (d[e[i].to]==-1 && e[i].c){
                d[e[i].to]=d[h]+1;
                q.push(e[i].to);
            }
        }
    }
    return d[T]!=-1;
}

int dinic(int x,int flow){
    if (x==T) return flow;int a,used=0;
    goedge(i,x){
        if (d[e[i].to]==d[x]+1 && e[i].c){
            a=dinic(e[i].to,min(flow-used,e[i].c));
            e[i].c-=a,e[i^1].c+=a,used+=a;
            if (used==flow) return flow;
        }
    }
    if (!used) d[x]=-1;
    return used;
}

int main(){
    while (scanf("%d%d",&n,&m)!=EOF){
        S=2*n,T=2*n+1;
        For(i,1,m) u[i]=read(),v[i]=read();
        ans=n;
        For(i,0,n-1) For(j,i+1,n-1){
            build(i,j);
            int sum=0;
            while (bfs()) sum+=dinic(S,inf);
            ans=min(ans,sum);
        }
        printf("%d\n",ans);
    }
    return 0;
}

引用原文戳我

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。