这道题,要注意,写网络流的时候,最好最好不要破坏原来的矩阵,尤其是需要多次求一个图的最大流的时候。
这道题,是求一个图的最小割点数。开始以为有专门的算法可以用呢,不过没有找到,然后想拆点求最小割,但是不知道无向图应该怎么建,如果正常拆点的话,拆出来的图还是一个有向图,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);
}
}