网络流

匿名 (未验证) 提交于 2019-12-02 23:48:02

网络流=带反悔的贪心。――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

最小路径覆盖。
先将每个点拆成一个入点和一个出点。
$$

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!