POJ 1966 Cable TV Network (用最大流求最小割点数量)

这道题,要注意,写网络流的时候,最好最好不要破坏原来的矩阵,尤其是需要多次求一个图的最大流的时候。

这道题,是求一个图的最小割点数。开始以为有专门的算法可以用呢,不过没有找到,然后想拆点求最小割,但是不知道无向图应该怎么建,如果正常拆点的话,拆出来的图还是一个有向图,SW只是解决无向图的情况,很纠结!要是哪位大牛路过知道有更好的算法的话,跪求指点!

这道题的做法就是,拆点,将每个点拆为入点和出点,入点和出点之间流量为1,其他点之间流量为INF,同时要注意的是,源点和汇点是不能被删掉的,所以,当你要决定哪一个点是源点或汇点的时候,记得把相应的出点和入点之间的流量改INF,因为能被删掉的一定是流量为1的边,也就是其他的点;还要注意的是,不要枚举相连的点,也就是在没有拆点之前,是相邻的点,因为在拆点之后,确定了源点和汇点,并更改了出点和入点之间的流量,那么最大流必然大于等于INF,而且相邻的点求割点,典型的没有意义,就像是一共两个点,两个点之间有一条边,删除最少的使得这个图不两通,根据题意,就是最少删除两个,把这种情况放到一个图里面,不也一样吗!所以枚举不相邻的点作为源点和汇点,一次求最大流,找出最小的最大流,就是答案了!

这里还要注意对特殊数据的特殊处理:

有一个点和0条边的,输出1;

有0个点和0条边的,输出0;

图本身就是不连通的,输出0;

以上这三情况,就不用算了。

最后,如果最小的最大流为INF,那么说明,这个图要想不连通,就删除所有点,输出n。

代码:(这里用SAP算法,但是经过实践,另外的那个算法(我不记得名字了,就是bfs找增广路)也能过)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cctype>
#include <queue>
using namespace std;

const int INF = 0x3fffffff;
const int N = 110;
int n, m, u, v, flag, g[N][N], f[N];
char s[10], ch;
int gap[N], dis[N], pre[N], cur[N];
int a[N], flow[N][N];

/*int Maxflow( int S, int E, int nodenum )
{
    queue<int> q;
    int maxflow = 0;
    memset(flow, 0, sizeof(flow));
    while ( 1 ) {
        memset(a, 0, sizeof(a));
        a[S] = INF;
        q.push(S);
        while ( !q.empty() ) {
            int u = q.front(); q.pop();
            for ( int v = 0; v < nodenum; ++v ) 
                if ( !a[v] && g[u][v] > flow[u][v] ) {
                    pre[v] = u;
                    q.push(v);
                    a[v] = min( a[u], g[u][v] - flow[u][v] );
                }
        }
        if ( a[E] == 0 ) break;
        for ( int u = E; u != S; u = pre[u] ) {
            flow[pre[u]][u] += a[E];
            flow[u][pre[u]] -= a[E];
        }
        maxflow += a[E];
    }
    return maxflow;
}*/
int Maxflow( int S, int E, int nodenum )
{
    memset( cur, 0, sizeof(cur));
    memset( dis, 0, sizeof(dis));
    memset( gap, 0, sizeof(gap));
    memset( flow,0, sizeof(flow));
    int u = pre[S] = S, aug = -1, maxflow = 0;
    gap[0] = nodenum;
    while ( dis[S] < nodenum ) {
loop:
        for ( int v = cur[u]; v < nodenum; v++ ) 
            if ( g[u][v]-flow[u][v] && dis[u]==dis[v]+1 ){
                if ( aug == -1 || aug > g[u][v]-flow[u][v] ) aug = g[u][v]-flow[u][v];
                pre[v] = u;
                u = cur[u] = v;
                if ( v == E ) {
                    maxflow += aug;
                    for ( u = pre[u]; v != S; v = u, u = pre[u] ){
                       flow[u][v] += aug;
                       flow[v][u] -= aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        int mindis=nodenum - 1;
        for ( int v = 0; v < nodenum; v++ ) 
            if ( g[u][v]-flow[u][v] && mindis > dis[v] ){
                cur[u] = v;
                mindis = dis[v];
            }
        if ( (--gap[dis[u]]) == 0 ) break;
        gap[dis[u] = mindis+1] ++;
        u = pre[u];
    }
    return maxflow;
}

int find( int x ) 
{
    return f[x] == x ? x : (f[x] = find(f[x]));
}
void init()
{
    memset( g, 0, sizeof(g));
    for ( int i = 0; i < n; ++i ) f[i] = i;
    while ( m ) {
        ch = getchar();
        if ( ch == ')' ) {
           m--;
           g[u][u+n] = g[v][v+n] = 1;
           g[u+n][v] = g[v+n][u] = INF;
           int a = find(u);
           int b = find(v);
           if ( a!=b ) f[a] = b;
        }
        if ( ch == '(' ) u = 0, flag = 1;
        if ( ch == ',' ) v = 0, flag = 2;
        if ( isdigit(ch) ) 
           if ( flag == 1 ) u = u * 10 + ( ch - '0' );
           else if ( flag == 2 ) v = v * 10 + ( ch -'0' );
   }
}
int main()
{
    while ( scanf("%d%d", &n, &m) != EOF ) {
       if ( m == 0 ) {
           if ( n == 1 ) printf("1\n");
           else printf("0\n");
           continue;
       }
       init();
       int is = 0, ans = INF;
       for ( int i = 0; i < n; ++i ) if ( f[i] == i ) {
           is++;
           if ( is > 1 ) break;
       }
       if ( is > 1 ) {
           printf("0\n");
           continue;
       }
       for ( int i = 0; i < n; ++i )
           for ( int j = i + 1; j < n; ++j ) 
               if ( !g[i+n][j] ) {
                   g[i][i+n] = INF;
                   g[j][j+n] = INF;
                   int tmp = Maxflow( i, j+n, 2*n );
                   //cout << i << ' ' << j << ' ' << tmp << endl;
                   if ( ans > tmp ) ans = tmp;
                   g[i][i+n] = g[j][j+n] = 1;
               }
       if ( ans == INF ) ans = n;
       printf("%d\n", ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值