考虑把每条边必须经过的流量拆出来,那么就变成了某些边必须满流,求一组可行流。我们把必须满流的边拉出来,假想一个源汇 s,t,每条边起点连向 s,t 连向终点,跑 s 到 t 的最大流,若拉出来的边满流就找到了一组可行流。如果本来就有源汇,那么从 t 向 s 连流量正无穷的边,就转成无源汇网络流了。
先求出一个可行流,把连向超级源汇的边删掉,再从 s 到 t 求残余网络最大流。
拆满流边后,先不加 t 到 s 流量正无穷的边,跑出一个最大流,再加上 t 到 s 的边跑最大流,t 到 s 这条边的流量就是最小流。感性理解一下,加 t 到 s 边之前跑最大流会尽可能填满环流。
例题:luogu清理雪道
ZJOI2011 最小割(最小割树)
每次随便选择两个点,用边权为他们之间的最小割的边把他们连起来,再把点集按照最小割的划分分成两个部分,每个部分递归进行。这样会形成一棵树的结构,两个点的最小割就是树上两点间的最小边权。这道题是模板。
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
struct edge{
int to,next,pre,flow;
}ed[6010];
const int inf=2e9;
int head[151],sz,deep[151],ans[151][151],p[151],q[151],node[151],S,T,vis[151],n,m;
inline void add_edge(int from,int to,int flow)
{
ed[++sz].to=to;
ed[sz].next=head[from];
ed[sz].flow=flow;
ed[sz].pre=flow;
head[from]=sz;
}
inline int read()
{
char c=getchar();int x=0,flag=1;
while(!isdigit(c)) flag*=c=='-'?-1:1,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*flag;
}
bool bfs()
{
memset(deep,0,sizeof(deep));
queue q;
deep[S]=1;
q.push(S);
while(!q.empty())
{
int u=q.front();
q.pop();
if(u==T) return true;
for(int i=head[u];~i;i=ed[i].next)
{
int v=ed[i].to;
if(ed[i].flow&&!deep[v])
{
deep[v]=deep[u]+1;
q.push(v);
}
}
}
return false;
}
int dfs(int u,int come)
{
if(u==T) return come;
int rest=come;
for(int i=head[u];~i;i=ed[i].next)
{
int v=ed[i].to;
if(ed[i].flow&&deep[v]==deep[u]+1)
{
int new_flow=dfs(v,min(rest,ed[i].flow));
ed[i].flow-=new_flow;
ed[i^1].flow+=new_flow;
rest-=new_flow;
if(!rest) break;
}
}
if(rest) deep[u]=-1;
return come-rest;
}
int dinic()
{
int ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
void cut(int u)
{
vis[u]=1;
for(int i=head[u];~i;i=ed[i].next)
{
int v=ed[i].to;
if(!vis[v]&&ed[i].flow) cut(v);
}
}
void solve(int l,int r)
{
if(l==r) return;
for(int i=0;i<=sz;i++) ed[i].flow=ed[i].pre;
memset(vis,0,sizeof(vis));
S=node[l],T=node[r];
int tmp=dinic();
cut(node[l]);
for(int i=1;i<=n;i++)
{
if(vis[i])
{
for(int j=1;j<=n;j++)
if(!vis[j]) ans[i][j]=ans[j][i]=min(ans[i][j],tmp);
}
}
int s=0,t=0;
for(int i=l;i<=r;i++)
{
if(vis[node[i]]) p[++s]=node[i];
else q[++t]=node[i];
}
int k=l-1;
for(int i=1;i<=s;i++) node[++k]=p[i];
for(int i=1;i<=t;i++) node[++k]=q[i];
solve(l,l+s-1);
solve(l+s,r);
}
void init()
{
memset(head,-1,sizeof(head));
memset(ans,0x7f,sizeof(ans));
sz=-1;
}
int main()
{
int T=read();
while(T--)
{
init();
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
add_edge(u,v,w);
add_edge(v,u,w);
}
for(int i=1;i<=n;i++) node[i]=i;
solve(1,n);
int mm=read();
for(int i=1;i<=mm;i++)
{
int x=read(),sum=0;
for(int j=1;j<=n;j++)
for(int k=j+1;k<=n;k++) if(ans[j][k]<=x) sum++;
cout<
AHOI2009 最小割
题意:判断每条边是否可能在最小割上,是否一定在最小割上。
先跑一个最大流,边 (u,v) 可能在最小割上的边的条件是:1.满流。2.残余网络中不存在 u 到 v 的路径。因为如果存在一个流量,那么这条边不会作为瓶颈。实现上第二个条件等价于判断残余网络上 u,v 是否在一个强连通分量里。
边 (u,v) 一定在最小割上的条件是:1.满流。2.残余网络中 s 能到达 u,v 能到达 t。只有这样 (u,v) 这条边才是不可代替的。实现上第二个条件等价于 u 和 s 在一个联通分量里,v 和 t 在一个联通分量里。
#include
#define ll long long
using namespace std;
const int N=40010,M=60010;
struct edge{
int to,next,flow;
}ed[M<<1];
int n,m,S,T,sz=1,head[N],vis[N],tim,deep[N],q[N],low[N],dfn[N],st[N],top,inst[N],scc,bel[N],u[M],v[M],ind[M];
void add_edge(int from,int to,int flow)
{
ed[++sz].to=to;
ed[sz].flow=flow;
ed[sz].next=head[from];
head[from]=sz;
}
int read()
{
int x=0;char flag='*',c=getchar();
while(!isdigit(c)) flag=c,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return flag=='-'?-x:x;
}
bool bfs()
{
int h=1,t=0;
q[++t]=S;
tim++;
deep[S]=1;
vis[S]=tim;
while(t>=h)
{
int u=q[h++];
for(int i=head[u];i;i=ed[i].next)
{
int v=ed[i].to;
if(vis[v]!=tim&&ed[i].flow)
{
vis[v]=tim;
deep[v]=deep[u]+1;
if(v==T) return true;
q[++t]=v;
}
}
}
return false;
}
int dfs(int u,int come)
{
if(u==T) return come;
int rest=come;
for(int i=head[u];i;i=ed[i].next)
{
int v=ed[i].to;
if(vis[v]==tim&&deep[v]==deep[u]+1&&ed[i].flow)
{
int nf=dfs(v,min(rest,ed[i].flow));
ed[i].flow-=nf;
ed[i^1].flow+=nf;
rest-=nf;
if(!rest) break;
}
}
if(rest) deep[u]=-1;
return come-rest;
}
void tarjan(int u)
{
inst[u]=1;
st[++top]=u;
low[u]=dfn[u]=++tim;
for(int i=head[u];i;i=ed[i].next)
{
int v=ed[i].to;
if(!ed[i].flow) continue;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(inst[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
scc++;
while(1)
{
int now=st[top--];
bel[now]=scc;
inst[now]=0;
if(now==u) break;
}
}
}
int main()
{
n=read(),m=read(),S=read(),T=read();
for(int i=1;i<=m;i++)
{
u[i]=read(),v[i]=read();int c=read();
ind[i]=sz+1;
add_edge(u[i],v[i],c);
add_edge(v[i],u[i],0);
}
while(bfs()) dfs(S,1e9);
tim=0;
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=m;i++)
{
int x=u[i],y=v[i];
if(ed[ind[i]].flow==0)
{
if(bel[x]==bel[y]) puts("0 0");
else if(bel[S]==bel[x]&&bel[T]==bel[y]) puts("1 1");
else puts("1 0");
}
else puts("0 0");
}
return 0;
}