给以一个图和两个点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; }