UVA1660 电视网络 Cable TV Network(网络流,最小割)

题目链接

题意翻译
电视电缆网络的继电器之间的连接是双向的。如果任意两个继电器之间都连通,那么这个网络就是连通的,否则不连通。特别地,一个空网络或只有一个继电器的网络是连通的。
定义一个有n个继电器的网络的安全指数f为
如果不管移除几个继电器,网络都连通,f=n
使网络不连通至少要移除的继电器数
给出t(t≤20)个网络,求每个网络的安全指数(每个网络的继电器数≤50)。

UVA1660 电视网络 Cable TV Network(网络流,最小割)_第1张图片

枚举两个不直接连通的点 S 和 T ,求在剩余的 n−2 个节点中最少去掉多少个可以使 S 和 T 不连通,在每次枚举的结构中取 min 就是本题的答案。

运用点边转化技巧

把原来无向图中的每个点 x ,拆成入点 x 和出点 x′。在无向图中删去一个点⇔在网络中断开 (x,x′) 。对 ∀ x ≠ S , x ≠ T \forall x \neq S,x \neq T x=S,x=T
连有向边 (x,x′),容量为 1 。对原无向图的每条边 (x,y) ,连有向边 (x’,y),(y’,x),容量为 + ∞ +\infty +(防止割断)。

因为原来要删点,那么与这个点相连的若干条边都要切掉比较麻烦,那么直接将点 x x x 转换为入点 x x x 和出点 x ′ x' x 并将他们连起来,这样在想要删掉x这个点的时候只需要将边 x − > x ′ x->x' x>x这一条边删掉即可。
最小割中设置容量为 + ∞ +∞ + 的边具有“防止割断”的含义。

其他所有的相连的边都置为INF,标记不可割断,拆的是单个点自己和自己的入点和出点的边。

#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long ll;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 100;
const int M = 5e4+7;

int s1,t1,n,m;
int head[N<<1],ver[M],nex[M],edge[M],tot;
int a[N * N],b[N * N],deep[N<<1];


inline void add(int x,int y,int z){//正边反边
    ver[++tot] = y;edge[tot] = z;
    nex[tot] = head[x];head[x] = tot;
    ver[++tot] = x;edge[tot] = 0;
    nex[tot] = head[y];head[y] = tot;
}

inline bool bfs(){
    memset(deep,0,sizeof deep);
    queue<int>q;
    q.push(s1);
    deep[s1] = 1;//分层
    while(q.size()){
        int x = q.front();
        q.pop();
        for(int i = head[x];i;i = nex[i]){
            int y = ver[i],z = edge[i];//剩余容量>0才属于残量网络
            if(z > 0 && !deep[y]){//不只是更新deep数组,是在残量网络上更新deep数组
                q.push(y);
                deep[y] = deep[x] + 1;
                if(y == t1)return true;
            }
        }
    }
    return false;
}

inline int dinic(int x,int flow){
    if(x == t1)return flow;
    int res = flow;
    for(int i = head[x];i && res;i = nex[i]){
        int y = ver[i],z = edge[i];
        if(z > 0 && (deep[y] == deep[x] + 1)){
            int k = dinic(y,min(res,z));
            if(!k)deep[y] = 0;
            edge[i] -= k;
            edge[i ^ 1] += k;
            res -= k;
        }
    }
    return flow - res;
}

int main(){
    while(cin>>n>>m){
        for(int i = 0;i < m;++i){
            char str[20];
            scanf("%s",str);
            a[i] = b[i] = 0;
            int j;
            for(j = 1;str[j] != ',';j++)
                a[i] = a[i] * 10 + (str[j] - '0');
            for(j++;str[j] != ')';j++)
                b[i] = b[i] * 10 + (str[j] - '0');
        }
        int ans = INF;
        for (s1 = 0; s1 < n; s1++)
		for (t1 = 0; t1 < n; t1++)
        if(s1 != t1){
            memset(head,0,sizeof head);
            tot = 1;
            int maxflow = 0;
            for(int i = 0;i < n;++i){
                if(i == s1 || i == t1)//i是入点,i+n是出点
                     add(i,i + n,INF);//防止被割断
                else add(i,i + n,1);
            }
            for(int i = 0;i < m;++i){
                add(a[i] + n,b[i],INF);//不能割
                add(b[i] + n,a[i],INF);
            }
            while(bfs()){
                int num;
                while((num = dinic(s1,INF)))
                    maxflow += num;
            }
            ans = min(ans,maxflow);
        }
        if(n <= 1 || ans == INF)ans = n;
        cout<<ans<<endl;
    }
    return 0;
}

你可能感兴趣的:(#,最小割)