题目大意:
给定一个无向图与两个点,问最少去掉几个点(不能去掉给定的点)能使这两个点不连通,并输出字典序最小的方案。
简单题解:
这个问题是要求该图的最小点割集,可以使用最小割最大流定理。
但是最小割是边的割,该怎么办呢?
拆点!将每个点拆成入点和出点。所有入边连到入点上,出边从出点连出,流量均为+INF。再在入点和出点间连一条流量为1的边。然后做最大流即为答案。
至于输出最小字典序方案的问题,可以参考milk6,从小到大枚举删点,若删去后流量减少了1,则输出。
我的代码:
1 /* 2 ID:t-x.h1 3 LANG:C++ 4 TASK:telecow 5 */ 6 #include<cstdio> 7 #include<cstring> 8 #define min(x,y) ((x)<(y)?(x):(y)) 9 FILE *fi=fopen("telecow.in","r"),*fo=fopen("telecow.out","w"); 10 const int MAXn=2*(100+9),INF=99999999; 11 int n,s,t,g[MAXn][MAXn],r[MAXn][MAXn],save[MAXn][MAXn]; 12 int gap[MAXn],d[MAXn]; 13 int sap(int u,int flow) 14 { 15 if(u==t) 16 return flow; 17 int i,tmp,ans=0; 18 for(i=1;i<=2*n;++i) 19 if(r[u][i] && d[u]==d[i]+1) 20 { 21 tmp=sap(i,min(r[u][i],flow-ans)); 22 ans+=tmp,r[u][i]-=tmp,r[i][u]+=tmp; 23 if(ans==flow) 24 return ans; 25 } 26 if(d[s]>=2*n) 27 return ans; 28 if(!--gap[d[u]]) 29 d[s]=2*n; 30 ++gap[++d[u]]; 31 return ans; 32 } 33 int maxflow() 34 { 35 int tot=0; 36 memcpy(r,g,sizeof(r)); 37 memset(gap,0,sizeof(gap)); 38 memset(d,0,sizeof(d)); 39 for(gap[0]=2*n;d[s]<2*n;) 40 tot+=sap(s,INF); 41 return tot; 42 } 43 int main() 44 { 45 int m,i,j,k; 46 bool flag=1; 47 fscanf(fi,"%d%d%d%d",&n,&m,&s,&t); 48 s*=2,t=t*2-1; 49 for(i=1;i<=n;++i) 50 g[2*i-1][2*i]=1; 51 for(i=1;i<=m;++i) 52 { 53 fscanf(fi,"%d%d",&j,&k); 54 g[2*j][2*k-1]=g[2*k][2*j-1]=INF; 55 } 56 fprintf(fo,"%d\n",k=maxflow()); 57 memcpy(save,r,sizeof(r)); 58 for(i=1;i<=n;++i) 59 if(!save[2*i-1][2*i] && i!=s/2 && i!=(t+1)/2) 60 { 61 g[2*i-1][2*i]=0; 62 if(maxflow()+1==k) 63 { 64 memcpy(save,r,sizeof(r)); 65 --k; 66 if(flag) 67 fprintf(fo,"%d",i),flag=0; 68 else 69 fprintf(fo," %d",i); 70 } 71 else 72 g[2*i-1][2*i]=1; 73 } 74 fputc(10,fo); 75 fclose(fi); 76 fclose(fo); 77 return 0; 78 }