网络流
一、最大流
BFS Edmonds-Karp算法
增广路定理:
只要残量网络中存在增广路,流量就可以增大。如果残量网络中不存在增广路,则当前流是最大流。
(希望代码没有抄错。。。
#include <vector> #include <queue> #include <cstring> using namespace std; const int maxn=100010; const int INF=0x3f3f3f3f; struct edge{ int from,to,cap,flow; edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){} }; struct Edmonds_Karp{ int n,m; vector<edge>e; //边数的两倍 vector<int>G[maxn]; //邻接表,G[i][j]表示从节点i出发的第j条边在e数组中的序号 int a[maxn]; //从起点s到i点的残量最小值(可改进量) int p[maxn]; //i点的入弧编号 void init(int n){ for(int i=0;i<n;++i){ G[i].clear(); } e.clear(); } void add_edge(int from,int to,int cap){ e.push_back(edge(from,to,cap,0)); e.push_back(edge(to,from,0,0));//反向弧 m=e.size(); G[from].push_back(m-2);//每条弧和对应的反向弧保存在一起,序号是连着的 G[to].push_back(m-1); } int Maxflow(int s,int t){ int flow=0; for(;;){ memset(a,0,sizeof(a)); queue<int>q; q.push(s); a[s]=INF; while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<G[x].size();++i){ edge& r=e[G[x][i]]; if(!a[r.to]&&r.cap>r.flow){ p[r.to]=G[x][i]; a[r.to]=min(a[x],r.cap-r.flow); q.push(r.to); } } if(a[t]) break; } if(!a[t]) break; for(int u=t;u!=s;u=e[p[u]].from){ e[p[u]].flow+=a[t]; e[p[u]^1].flow-=a[t];//p[u]的反向边的序号是p[u]^1 } flow+=a[t]; } return flow; } };
Dinic 算法
洛谷3376 网络最大流(模板)
例子理解反悔:https://www.cnblogs.com/DavidJing/p/10713197.html
https://www.cnblogs.com/Eleven-Qian-Shan/p/13274900.html
#include <iostream> #include <vector> #include <queue> #include <cstring> using namespace std; typedef long long ll; const int maxn=5555; const ll INF=0x3f3f3f3f3f3f3f3f; struct edge{ int from,to; ll cap,flow; edge(int u,int v,ll c,ll f):from(u),to(v),cap(c),flow(f){} }; vector<edge>e; vector<int>G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; int s,t; void add_edge(int from,int to,ll cap){ e.push_back(edge{from,to,cap,0}); e.push_back(edge{to,from,0,0}); int m=e.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS(){ memset(vis,0,sizeof(vis)); queue<int>q; q.push(s); d[s]=0; vis[s]=1; while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<G[x].size();++i){ edge& r=e[G[x][i]]; if(!vis[r.to]&&r.cap>r.flow){ vis[r.to]=1; d[r.to]=d[x]+1; q.push(r.to); } } } return vis[t]; } ll DFS(int x,ll a){//返回值long long if(x==t||a==0){ return a; } ll flow=0,f; for(int& i=cur[x];i<G[x].size();++i){ edge& r=e[G[x][i]]; if(d[x]+1==d[r.to]&&(f=DFS(r.to,min(a,r.cap-r.flow)))>0){ r.flow+=f; e[G[x][i]^1].flow-=f; flow+=f; a-=f; if(a==0) break; } } cout<<flow<<endl; return flow; } ll Maxflow(int s,int t){//返回值long long ll flow=0; while(BFS()){ memset(cur,0,sizeof(cur)); flow+=DFS(s,INF); } return flow; } int main(){ int n,m,u,v; ll w; cin>>n>>m>>s>>t; for(int i=0;i<m;++i){ cin>>u>>v>>w; add_edge(u,v,w); } cout<<Maxflow(s,t)<<endl; return 0; }
二、最大流最小割定理
在一个网络流中,能够从源点到达汇点的最大流量等于如果从网络中移除就能够导致网络流中断的边的集合的最小容量和。即在任何网络中,最大流的值等于最小割的容量。
三、最小费用最大流算法(MCMF)
费用值可正可负。
和Edmonds-Karp算法类似,但每次用Bellman-Ford算法而非BFS找增广路。
(希望代码没抄错。。。不愧是我。。果然抄错了。。debug一下午还没看出。。
#include <vector> #include <queue> #include <cstring> using namespace std; const int maxn=100010; const int INF=0x3f3f3f3f; struct edge{ int from,to,cap,flow,cost;//cost为该条边单位流量所需的费用 edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){} }; struct MCMF{ int n,m,s,t; vector<edge>e; vector<int>G[maxn]; int a[maxn];//可改进量 int p[maxn];//上一条弧 int d[maxn];//BellmanFord int inq[maxn];//是否在队列中 void init(int n){ this->n=n; for(int i=0;i<n;++i){ G[i].clear(); } e.clear(); } void add_edge(int from,int to,int cap,int cost){ e.push_back(edge(from,to,cap,0,cost)); e.push_back(edge(to,from,0,0,-cost));//反向弧 m=e.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BellmanFord(int s,int t,int& flow,int& cost){ for(int i=0;i<n;++i) d[i]=INF; memset(inq,0,sizeof(inq)); d[s]=0;inq[s]=1;p[s]=0;a[s]=INF; queue<int>q; q.push(s); while(!q.empty()){ int u=q.front(); q.pop();
inq[u]=0; for(int i=0;i<G[u].size();++i){ edge& r=e[G[u][i]]; if(r.cap>r.flow&&d[r.to]>d[u]+r.cost){ d[r.to]=d[u]+r.cost; p[r.to]=G[u][i]; a[r.to]=min(a[u],r.cap-r.flow); if(!inq[r.to]){ q.push(r.to); inq[r.to]=1; } } } } if(d[t]==INF) return false;//s-t不连通 flow+=a[t]; cost+=d[t]*a[t];//增广路径的每条边增加a[t]流量,d[t]为这些边总的费用,所以相乘即为总的费用增加量(类似于乘法分配律) int u=t; while(u!=s){ e[p[u]].flow+=a[t]; e[p[u]^1].flow-=a[t]; u=e[p[u]].from; } return true; } int Mincost(int s,int t){ int flow=0,cost=0; while(BellmanFord(s,t,flow,cost)); return cost; } };