拆点法,把一个点拆为两个,之间的流量为点的权值,这样求割点的集合就变为了求割边的集合。
#include<iostream> using namespace std; const int maxn=405; const int INF=0x7ffffff; int c[maxn][maxn],dep[maxn],q[maxn],flow[maxn][maxn],num[maxn],pre[maxn]; void bfs(int n,int s,int t) { int f=0,r=0,u,v; memset(dep,125,sizeof(dep)); memset(num,0,sizeof(num)); dep[t]=0; num[0]++; q[r++]=t; while(f<r) for(v=q[f++],u=1; u<=n;u++) if(flow[u][v]<c[u][v] && dep[u]>n) { dep[u]=dep[v]+1; q[r++]=u; num[dep[u]]++; } } int find_path(int n,int u)//找增广路 { for(int i=1;i<=n;i++) if(flow[u][i]<c[u][i]&&dep[u]==dep[i]+1) return i; return -1; } int relabel(int n,int u) //找不到增广路,重新标记 { int tmp=n; for(int i=1;i<=n;i++) if(flow[u][i]<c[u][i])//找残留流中从u出发最短的。。。 tmp=min(tmp,dep[i]+1); //这样就保证了所有点依然是最短的了 return tmp; } int sap(int n,int s,int t) { memset(pre,-1,sizeof(pre)); int u=s,max_flow=0,cost,x,v,k; bfs(n,s,t); while(dep[s]<n) //一定可以增广完 { v=find_path(n,u);//找从i出发的可行弧 if(v>=0) { pre[v]=u; u=v; if(v==t)//更新流 { cost=INF; for(k=t; k!=s; k=pre[k]) cost=min(cost,c[pre[k]][k]-flow[pre[k]][k]); for(k=t;k!=s; k=pre[k]) flow[pre[k]][k] += cost,flow[k][pre[k]] -=cost; max_flow += cost; u=s; //继续从头找 } } else //找不到可行弧 { x=relabel(n,u); //重新标记 num[x]++;num[dep[u]]--; //层次数量变化 if(num[dep[u]]==0) //处理断层 return max_flow; dep[u]=x; if(pre[u]!=s) u=pre[u]; } } return max_flow; } int main() { int n,s,t,i,j,w; bool flag=0; cin>>n>>s>>t; s=s+n; //新源点 for(i=1;i<=n;i++) { c[i][i+n]=1; for(j=1;j<=n;j++) { scanf("%d",&w); if(w) { if(i==s-n&&j==t) flag=true; //s能直接联系t; c[i+n][j]=INF; } } } if(flag) { printf("NO ANSWER!\n"); return 0; } int max_flow,cur_flow,ans[205],cnt; max_flow = sap(n*2,s,t); cnt=0; for(i=1;i<=n;i++) //要求字典序,贪心枚举 { if(i==s-n||i==t) continue; memset(flow,0,sizeof(flow)); c[i][i+n]=0; //删除边,再求最大流 cur_flow = sap(2*n,s,t); if(cur_flow<max_flow) //小于最大流,则i是最小割中 { ans[cnt++]=i; max_flow--; //减少流量 } else c[i][i+n]=1; //否则还原 if(max_flow<=0) break; } cout<<cnt<<endl; for(i=0;i<cnt;i++) printf("%d ",ans[i]); cout<<endl; system("pause"); return 0; }