网络流=带反悔的贪心。――517
个人认为网络流=最大流dinic/费用流板子+玄学意会建图。
对于每条边 \((u,v,w)\) ,建一条相应的反向边 \((v,u,0)\) 。
算法执行时,先从源点s bfs,看看到t最多能流多少,对于每个节点记录它的前驱节点,如果到t的流量不为0,那么从t回溯回s,将每条边的容量减去流量,其反向边的容量加上流量,然后把答案加上所有回溯到的边的流量;否则停止执行,返回结果。最坏复杂度 \(O(nm^2)\) 。
#include<bits/stdc++.h> #define maxn 10005 #define maxm 100005 #define INF 1050000000 using namespace std; template<typename tp> void read(tp& x){ x=0; char c=getchar(); bool sgn=0; while((c<'0'||c>'9')&&c!='-')c=getchar(); if(c=='-')sgn=1,c=getchar(); while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar(); if(sgn)x=-x; } template<typename tp> void write(tp x){ if(x<0)putchar('-'),write(-x); else{ if(x>=10)write(x/10); putchar(x%10+'0'); } } struct edge{ int to,next,w; }e[maxm<<1]; int head[maxn],cnte; void add(int u,int v,int w){ e[++cnte].to=v; e[cnte].w=w; e[cnte].next=head[u]; head[u]=cnte; } int n,m,s,t,pre[maxn],edg[maxn],flow[maxn]; bool bfs(){ for(int i=1;i<=n;i++)pre[i]=edg[i]=-1,flow[i]=INF; pre[s]=0; queue<int> q; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); if(u==t)break; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to; if(e[i].w>0&&pre[v]==-1){ pre[v]=u; edg[v]=i; flow[v]=min(flow[u],e[i].w); q.push(v); } } } return pre[t]!=-1; } int ek(){ int ans=0; while(bfs()){ int x=t; while(x!=s){ e[edg[x]].w-=flow[t]; e[edg[x]^1].w+=flow[t]; x=pre[x]; } ans+=flow[t]; } return ans; } signed main(){ read(n),read(m),read(s),read(t); for(int i=1;i<=n;i++)head[i]=-1; cnte=-1; for(int i=1;i<=m;i++){ int u,v,w; read(u);read(v);read(w); add(u,v,w); add(v,u,0); } write(ek()); return 0; }
解释不来,请上网搜索……
最坏复杂度 \(O(n^2m)\) 。head1[]
:当前弧优化,极小部分题会卡,如LOJ117,加了快10倍
#include<bits/stdc++.h> using namespace std; const int maxn=10003,maxm=200003,INF=1050000000; struct edge{int to,next,w;}e[maxm<<1]; int head[maxn],head1[maxn],cnte; void add(int u,int v,int w){e[++cnte].to=v,e[cnte].w=w,e[cnte].next=head[u],head[u]=cnte;} void addedge(int u,int v,int w){add(u,v,w),add(v,u,0);} int n,s,t,dep[maxn],q[maxn]; bool bfs(){ for(int i=1;i<=n;i++)dep[i]=0,head1[i]=head[i]; dep[s]=1; int *qhead=q,*qtail=q; *qtail++=s; while(qhead!=qtail){ int u=*qhead++; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to; if(e[i].w&&dep[v]==0){ dep[v]=dep[u]+1; *qtail++=v; } } } return dep[t]!=0; } int dfs(int u,int low){ if(low==0||u==t)return low; int flow=0; for(int &i=head1[u];~i;i=e[i].next){ int v=e[i].to; if(e[i].w&&dep[v]==dep[u]+1){ int tmp=dfs(v,min(low,e[i].w)); if(tmp==0)dep[v]=0; else{ flow+=tmp; low-=tmp; e[i].w-=tmp; e[i^1].w+=tmp; if(low==0)break; } } } return flow; } int dinic(){ int ans=0; while(bfs())ans+=dfs(s,INF); return ans; } int main(){ int m; scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1;i<=n;i++)head[i]=-1; cnte=-1; while(m--){ int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } printf("%d",dinic()); return 0; }
对于每条边 \((u,v,w,cost)\) ,建反向边 \((v,u,w,-cost)\)
该算法即在最大流ek的基础上把bfs改为spfa。为什么不能直接用dijkstra?因为有负环。
#include<bits/stdc++.h> #define maxn 5005 #define maxm 100005 #define INF 1050000000 using namespace std; struct edge{int to,next,w,cost;}e[maxm<<1]; int head[maxn],cnte; void add(int u,int v,int w,int cost){e[++cnte].to=v,e[cnte].w=w,e[cnte].cost=cost,e[cnte].next=head[u],head[u]=cnte;} void addedge(int u,int v,int w,int cost){add(u,v,w,cost),add(v,u,w,-cost);} int n,s,t,pre[maxn],edg[maxn],flow[maxn],maxflow,dis[maxn],mincost; bool vis[maxn]; bool spfa(){ for(int i=1;i<=n;i++)flow[i]=dis[i]=INF,vis[i]=0; pre[t]=-1; dis[s]=0; queue<int> q; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to; if(e[i].w>0&&dis[u]+e[i].cost<dis[v]){ dis[v]=dis[u]+e[i].cost; pre[v]=u; edg[v]=i; flow[v]=min(flow[u],e[i].w); if(!vis[v]){ vis[v]=1; q.push(v); } } } } return pre[t]!=-1; } void micmxf(){ while(spfa()){ int x=t; while(x!=s){ e[edg[x]].w-=flow[t]; e[edg[x]^1].w+=flow[t]; x=pre[x]; } maxflow+=flow[t]; mincost+=flow[t]*dis[t]; } } signed main(){ int m; scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=0;i<=n;i++)head[i]=-1; cnte=-1; for(int i=1;i<=m;i++){ int u,v,w,cost; scanf("%d%d%d%d",&u,&v,&w,&cost); addedge(u,v,w,cost); } micmxf(); printf("%d %d\n",maxflow,mincost); return 0; }
BZOJ1001
最朴素的建模。
直接在原图上源点 \(s=1\) ,汇点 \(t=n*m\) 跑最大流即可。
luogu2756
理解最大流和二分图匹配的关系。
本题先构造一个二分图,然后对于所有左侧点 \(i\) ,连边 \((s,i)\) ;对于所有右侧点 \(i\) ,连边 \((i,t)\) 。
luogu2764
最小路径覆盖。
先将每个点拆成一个入点和一个出点。
$$