POJ 1966 Cable TV Network【无向图点连通度 最小割 E-K算法求最大流】

题目描述:

给你一个无向图,问你最少删掉几个点,使这个图成不连通。

解题报告:

概念

 

(1)一个具有 N 个顶点的图,在去掉任意 k-1 个顶点后 (1<=K<=N) 所得的子图仍连通,

  而去掉 K 个顶点后的图不连通则称 G 是连通的, K 称作图 G 的点连通度,记作 K(G) 试设计

(2)相应地如果至少去掉 K 条边使这个图不连通,则 K 成为图的边连通度

 

边连通度:

  为每条边赋权值为1,然后求确定一点作为源点,枚举此点外的每个点作为汇点求最大流。

  也可以用stoer_wagner算法求得无向图的最小割

点连通度:

  求一个给定的无向图的点连通度,可以转换为求边连通度,怎么转换就如下所示:

 

将点连通度转化为边连通度时怎么建图呢:

1 .构造一个网络 N

若 G 为无向图:

   (1) 原 G 图中的每个顶点 v 变成 N 网中的两个顶点 v' 和 v" ,顶点 v' 至 v" 有一条弧(有向边)连接,弧容量为 1;

   (2) 原 G 图中的每条边 e = uv ,在 N 网中有两条弧 e’= u"v',e"=v"u' 与之对应, e' 弧容量为 ∞ , e" 弧容量为 ∞

   (3)A” 为源顶点, B' 为汇顶点

    注意:弧是有向边

若 G 为有向图:

   (1) 原 G 图中的每个顶点变成 N 网中的两个顶点 v’ 和 v” ,顶点 v' 至 v” 有一条弧连接,弧容量为 1

   (2) 原 G 图中的每条弧 e = uv 变成一条有向轨 u'u"v'v" ,其中轨上的弧 u"v' 的容量为 ∞;

   (3)A” 为源顶点, B' 为汇顶点

   

2 .指定一个源点 A" ,枚举汇点B',求 A" 到 B' 的最大流 F ,其中F最小的就是本题答案。

  注意在求出最大流=inf时说明图是强连通的,则需要去掉所有的点。

View Code
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;

#define MAXN 100 + 5
#define INF 0xfffffff

int n, m, f;
int flow[MAXN][MAXN];
int Edge[MAXN][MAXN];
int pre[MAXN];
bool mark[MAXN];

//最大流模版,不解释了。
void max_flow(int u, int v)
{
    f = 0;
    while(1) {
        memset(mark, 0, sizeof(mark));
        memset(pre, 0, sizeof(pre));

        queue<int> Q;
        mark[u] = 1;
        Q.push(u);
        while( !Q.empty() ) {
            int cnt = Q.front();
            Q.pop();

            if(cnt == v) {
                break;
            }

            for(int i = 0; i < n * 2; i++) {
                if(!mark[i] && flow[cnt][i] > 0) {
                    mark[i] = 1;
                    Q.push(i);
                    pre[i] = cnt;
                }
            }

        }
        if( !mark[v]) {
            break;
        }
        int minx = INF;
        for(int i = v; i != u; i = pre[i]) {
            minx = min(flow[pre[i]][i], minx);
        }

        for(int i = v; i != u; i = pre[i]) {
            flow[pre[i]][i] -= minx;
            flow[i][pre[i]] += minx;
        }
        f += minx;
    }
}

int main()
{
    while(scanf("%d %d%*c", &n, &m) != EOF) {
        memset(Edge, 0, sizeof(Edge));
        int k = -1; //k为源点。
        if(n == 1 && m == 0) {
            printf("1\n");
            continue;
        }
        if(m == 0) {
            printf("0\n");
            continue;
        }
        for(int i = 0; i < m; i++) {
            //注意输入的格式,因为这快儿RE了很多次。
            while(getchar()!='(');
            int u, v;
            scanf("%d,%d)", &u, &v);
            //图的转换,方法上面以给出。
            Edge[u][n + u] = 1;
            Edge[v][n + v] = 1;
            Edge[n + u][v] = INF;
            Edge[n + v][u] = INF;
            if( k == -1) {
                k = n + u;
            }
        }

        int ans = INF;
        //枚举0-n的点到源点的最大流,ans为其中最小的。
        for(int i = 0; i < n; i++) {
            if(i + n == k) {
                continue;
            }

            for(int j = 0; j < 2 * n; j++) {
                for(int t = 0; t < 2 * n; t++) {
                    flow[j][t] = Edge[j][t];
                }
            }

            max_flow(k, i);
            ans = min(f, ans);
        }

        //当ans == INF说明图是强连通的,所以要去掉所有的点。
        if(ans == INF) {
            printf("%d\n", n);
        } else {
            printf("%d\n", ans);
        }
    }
    return 0;
}

ps:这个题RE了好多次,输入是值得注意的地方,就是两条边之间有没有空格都行,所以一定要仔细读题。

posted @ 2012-07-24 22:26  小猴子、  阅读(1602)  评论(1编辑  收藏  举报