????? 無向(有向)圖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; }
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
