1. 程式人生 > >POJ 1966 Cable TV Network 【經典最小割問題】

POJ 1966 Cable TV Network 【經典最小割問題】

問題 desc als ron network har struct dfs return

Description

n個點的無向圖,問最少刪掉幾個點,使得圖不連通

n<=50 m也許可以到完全圖?

Solution

最少,割點,不連通,可以想到最小割。

發現,圖不連通,必然存在兩個點不連通。

枚舉源點匯點,要讓源點匯點不連通。源點匯點不能割掉

網絡建圖:

為了割的是邊,所以要點轉化成邊。

對於每個x,建立x‘=x+n,對於不是S、T的點(因為S、T不能割掉),x向x’連一條邊權為1的邊

對於原圖的邊e(x,y) x’->y 連接inf的邊,y‘->x連接inf的邊。

邊權保證割的是點,不是邊。

x‘->y連邊保證,想走這條邊,必須經過x。

源點匯點不唯一,所以要n^2枚舉。

但是要保證S,T不直接相連,否則不可能分開。

(如果欽定0是源點,枚舉匯點的話,可以hack掉

假設最優解必須割掉0,那麽就ans大了

但是數據水

然後每次重新建圖。

跑dinic最小割,所有的最小割取min即可。

題目一些小坑:

1.圖不連通?沒關系,可以枚舉得到最小割為0

2.m=0,同上

3.如果完全圖?沒有S、T滿足不直接相鄰,那麽一定就是n

fl記錄一下有沒有dinic過即可。

4.n=1?同上fl可以判斷。

Code

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include
<cstring> #include<queue> #define numb ch-‘0‘ using namespace std; const int N=52; const int inf=0x3f3f3f3f; int n,m; char ch; void rd(int &x){ x=0; while(!isdigit(ch=getchar())); for(x=numb;isdigit(ch=getchar());x=x*10+numb); } struct node{ int nxt,to; int w; }e[2*N*N+N];
int hd[2*N],cnt=1; bool con[N][N]; void add(int x,int y,int z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].w=z; hd[x]=cnt; } queue<int>q; int d[2*N]; int s,t; void pre(){ memset(hd,0,sizeof hd); cnt=1; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(j==i) continue; if(con[i][j]){ add(i+n,j,inf/2); add(j,i+n,0); } } if(i!=s&&i!=t) add(i,i+n,1),add(i+n,i,0); } } bool bfs(){ while(!q.empty())q.pop(); memset(d,0,sizeof d); d[s+n]=1; q.push(s+n); while(!q.empty()){ int x=q.front();q.pop(); for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(!d[y]&&e[i].w){ d[y]=d[x]+1; q.push(y); if(y==t) return 1; } } } return 0; } int lp=0; int dfs(int x,int flow){ if(x==t) return flow; int rest=flow; for(int i=hd[x];i&&rest;i=e[i].nxt){ int y=e[i].to; if(d[y]==d[x]+1&&e[i].w){ int k=dfs(y,min(rest,e[i].w)); if(!k) d[y]=0; rest-=k; e[i].w-=k; e[i^1].w+=k; } } return flow-rest; } int wrk(){ pre(); int ret=0; int flow; while(bfs()){ while(flow=dfs(s+n,inf)) ret+=flow; } return ret; } int ans; bool fl; void clear(){ memset(hd,0,sizeof hd); cnt=1; memset(con,0,sizeof con); ans=inf; fl=false; } int main() { int x,y; while(scanf("%d",&n)!=EOF){ clear(); scanf("%d",&m); for(int i=1;i<=m;i++){ rd(x);rd(y); x++;y++; con[x][y]=con[y][x]=1; } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(con[i][j]) continue; fl=true; s=i,t=j; int tmp=wrk(); ans=min(ans,tmp); } } if(!fl) ans=n; printf("%d\n",ans); } return 0; }

這個題,體現了“點邊轉化”,“容量inf”的處理思想。

點邊轉化:把點的信息轉移到邊上,或者邊信息轉移到點上。

點變成邊:拆點,兩個點之間的邊信息是點的信息。並且要保證,實際經過這個點,必須經過這個邊。

    一般從上面的點x‘向下面y連邊。

邊變成點:把邊拆成兩個,中間加一個點,記錄邊的信息。

POJ 1966 Cable TV Network 【經典最小割問題】