POJ 1966 Cable TV Network 【經典最小割問題】
阿新 • • 發佈:2018-09-09
問題 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 【經典最小割問題】