zoukankan      html  css  js  c++  java
  • Poj 1815 Friendship 枚举+求最小割

    给以一个图和两个点S,T,问你拿掉最少多少个点可以使得S和T不连通。输出点数并且输出拿掉的是哪些点,如果有多种方法就输出字典序最小的那个。

    这就是一个求最小点割集的问题。无向(有向)图G中,给定源点s和终点t,至少要删去多少个点(具体一点,删哪些点),使得s和t不连通。这个问题就是点连通度,也叫最小点割集

    解法其实理解起来不难,只要把图中的每一个点v拆成v',v''两个点,并建立<v',v''>权为1,这样就把最小点割集转化成求最小割的问题。

    对于原图的转化也很简单,对于原来的每条边,转化成这样的形式u'->u''->v'->v'',即连一条<u'',v'>的边,对于无向图只要反过来再连一次就好,这些边的容量当然为正无穷。然后只要求一次最小割,出来的结果自然就是要去掉多少个点了。

    对于这道题,建立源点s并且连边<s,S'>容量为正无穷,并且建立汇点t连边<T'',t>容量为正无穷,最后求一次最小割即可。

    对于一开始判定是不是无解的问题,显然只有S和T直接相连的时候才会无解,这个时候对应的边应该是<S'',T'>

    对于最后要输出字典序最小的方案,我只会用枚举的方法,从小到大依次删去每一个除了S和T之外的点,其实拆点了之后操作很简单,只要删去<v',v''>就好了,删去这个点之后求一下最小割,如果最小割变小了,则在解的集合里面加入这个点,直到流量最后变为0了。如果割没有变小别忘记把删去的边加回去。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <climits>
    #include <string>
    #include <iostream>
    #include <map>
    #include <cstdlib>
    #include <list>
    #include <set>
    #include <queue>
    
    using namespace std;
    
    typedef long long LL;
    const int maxn = 405;
    const int INF = INT_MAX / 2;
    int N,S,T,cap[maxn][maxn],flow[maxn][maxn],s,t;
    int mp[maxn][maxn];
    
    void build_graph() {
        memset(cap,0,sizeof(cap));
        s = 0; t = 2 * N + 1;
        cap[s][S] = INF;
        cap[T + N][t] = INF;
        for(int i = 1;i <= N;i++) {
            cap[i][i + N] = 1;
            for(int j = 1;j <= N;j++) if(mp[i][j] && i != j) {
                cap[i + N][j] = INF;
            }
        } 
        cap[S][S + N] = cap[T][T + N] = INF;
    }
    
    int level[maxn],q[maxn],qs,qe;
    bool bfs() {
        memset(level,0,sizeof(level));
        qs = qe = 0;
        q[qe++] = s;
        level[s] = 1;
        while(qs < qe) { int now = q[qs++]; if(now == t) break;
            for(int i = s;i <= t;i++) {
                if(cap[now][i] - flow[now][i] && !level[i]) {
                    q[qe++] = i; level[i] = level[now] + 1;
                }
            }
        }
        return level[t];
    }
    
    int dfs(int now,int alpha) {
        if(now == t) return alpha;
        int sum = 0;
        for(int i = s;i <= t && alpha;i++) {
            if(cap[now][i] - flow[now][i] && level[now] + 1 == level[i]) {
                int ret = dfs(i,min(alpha,cap[now][i] - flow[now][i]));
                flow[now][i] += ret; flow[i][now] -= ret;
                sum += ret; alpha -= ret;
            }
        }
        //printf("now is %d,sum is %d
    ",now,sum);
        if(sum == 0) level[now] = -1;
        return sum;
    }
    
    void solve() {
        if(cap[S + N][T]) {
            puts("NO ANSWER!");
            return;
        }
        int ansval = 0,cnt = 0;
        while(bfs()) ansval += dfs(S,INF);
        printf("%d
    ",ansval);
        //开始枚举
        for(int i = 1;i <= N;i++) if(i != S && i != T) {
            memset(flow,0,sizeof(flow));
            cap[i][i + N] = 0;
            int nowans = 0;
            while(bfs()) nowans += dfs(S,INF);
            if(nowans != ansval) {
                if(cnt != 0) putchar(' ');
                printf("%d",i);
                ansval = nowans;
                cnt++;
            } else cap[i][i + N] = 1;
            if(ansval == 0) break;
        }
        if(cnt) puts("");
    }
    
    int main() {
        while(~scanf("%d%d%d",&N,&S,&T)) {
            for(int i = 1;i <= N;i++) {
                for(int j = 1;j <= N;j++) {
                    scanf("%d",&mp[i][j]);
                }
            }
            build_graph();
            solve();
        }
        return 0;
    }
    

      

  • 相关阅读:
    数据仓库的直白概述
    Google准实时数据仓库Mesa(一)
    活动预告丨易盾CTO朱浩齐将出席2018 AIIA大会,分享《人工智能在内容安全的应用实践》
    3招搞定APP注册作弊
    【0门槛】PR稿的自我修养
    Hive中文注释乱码解决方案(2)
    Hive中文注释乱码解决方案
    网易考拉Android客户端网络模块设计
    有运气摇号来不及挑选?网易有数帮你科学选房
    selenium下拉框踩坑埋坑
  • 原文地址:https://www.cnblogs.com/rolight/p/3873082.html
Copyright © 2011-2022 走看看