无向(有向)图G中,给定源点s和终点t,至少要删去多少个点(具体一点,删哪些点),使得s和t不连通。这个问题就是
点连通度
,也叫
最小点割集
。
一般最小点割转化到最小边割上,将原图中的点v拆成v'和v'',且w(v,v'')=1。对于原图中的有向边(u,v),则有w(u'',v')=INF;若是无向边,则还要加上边:w(v'',v')=INF。然后求以s''为源点,t'为汇点的最大流。 maxflow即为最少需要删的点数,割边集对应了具体删的点的一组解 。
值得 注意 的是最小点割的解有很多,如下图:
最小点割的方案有4种:(4,3),(4,1),(2,3),(2,1)。若按上述方法求解,最小割点集为(4,3)。
例: POJ1815
题意: 给出一个图,问要删去多少个点,才能使得从1到不了n。显然这是个最小点割问题。但题目又要求,如果有多种方案,输出序号最小的一种。
解: 显然,如果s和t直接相连,则无解。求最小点割集方法并不难,取割边集即可,关键是如何满足题意"序号最小的一种"。有一种简单的贪心:从2到n-1枚举删除每一个点,看删除之后流量是否发生变化,若变化,则这点就算在解中;否则恢复这个点。过程一直进行到maxflow=0为止。
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int INF = 0x7fffffff; const int maxv = 410; const int maxe = maxv*maxv*10; int n; int g[205][205]; struct Edge { int v; int next; int flow; }; Edge e[maxe]; int head[maxv],edgeNum; int now[maxv],d[maxv],vh[maxv],pre[maxv],preh[maxv]; int result[maxv]; bool cut[maxv]; void addEdge(int a,int b,int c) { e[edgeNum].v = b; e[edgeNum].flow = c; e[edgeNum].next = head[a]; head[a] = edgeNum++; e[edgeNum].v = a; e[edgeNum].flow = 0; e[edgeNum].next = head[b]; head[b] = edgeNum++; } void Init() { edgeNum = 0; memset(head,-1,sizeof(head)); memset(d,0,sizeof(d)); } int sap(int s,int t,int n) //源点,汇点,结点总数 { int i,x,y; int f,ans = 0; for(i = 0; i < n; i++) now[i] = head[i]; vh[0] = n; x = s; while(d[s] < n) { for(i = now[x]; i != -1; i = e[i].next) if(e[i].flow > 0 && d[y=e[i].v] + 1 == d[x]) break; if(i != -1) { now[x] = preh[y] = i; pre[y] = x; if((x=y) == t) { for(f = INF,i=t; i != s; i = pre[i]) if(e[preh[i]].flow < f) f = e[preh[i]].flow; ans += f; do { e[preh[x]].flow -= f; e[preh[x]^1].flow += f; x = pre[x]; }while(x!=s); } } else { if(!--vh[d[x]]) break; d[x] = n; for(i=now[x]=head[x]; i != -1; i = e[i].next) { if(e[i].flow > 0 && d[x] > d[e[i].v] + 1) { now[x] = i; d[x] = d[e[i].v] + 1; } } ++vh[d[x]]; if(x != s) x = pre[x]; } } return ans; } void makeGraph() { int i,j; Init(); for(i = 1; i <= n; i++) { for(j = 1; j <= n; j++) { if(g[i][j]) { addEdge(i+n,j,INF); addEdge(j+n,i,INF); } } } for(i = 1; i <= n; i++) { if(!cut[i]) addEdge(i,i+n,1); else addEdge(i,i+n,0); } } int main() { int i,j; int s,t; scanf("%d %d %d",&n,&s,&t); memset(cut,0,sizeof(cut)); for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) scanf("%d",&g[i][j]); if(g[s][t]) { printf("NO ANSWER!"); return 0; } makeGraph(); int flow = sap(s+n,t,2*n); printf("%d\n",flow); int cnt = 0; for(i = 1; i <= n && flow; i++) { if(i==s||i==t) continue; cut[i] = true; makeGraph(); if(sap(s+n,t,2*n) == flow-1) { flow--; result[cnt++] = i; } else cut[i] = false; } for(i = 0; i < cnt; i++) printf("%d ",result[i]); printf("\n"); return 0; }