ACM網路流模板(更新ing...)
阿新 • • 發佈:2019-01-09
1、 最大流
- FF、EK、Dinic演算法比較
- Dinic演算法
2、二分圖匹配
- 匈牙利演算法
3、最小費用流
- SPFA演算法求解
- Dijkstra演算法求解
1、最大流( FF、EK、Dinic演算法比較)
- Ford-Fulkerson演算法:通過dfs不斷尋找增廣路,複雜度O(V·E^2),效率較低。
- Edmonds-Karp演算法:通過bfs不斷尋找增廣路,複雜度O(V·E^2),效率比FF略好,但是還是較低。
- Dinic演算法:首先bfs預處理出層次圖,然後在層次圖上dfs進行增廣,複雜度 O(V^2·E),時間效率較好。
2、最大流(Dinic演算法)
描述:
Dinic演算法總是尋找最短的增廣路,並沿著它增廣。與之相對,EK演算法執行完一次bfs增廣後,要重新從源點s開始尋找另一條增廣路,而在Dinic演算法中,只需一次bfs就可以實現多次增廣,效率提高。
複雜度:
O(V^2·E)
int V,E; struct edge{ //用於表示邊的結構體(終點,容量,反向邊) int to,cap,rev; edge(int t,int c,int r):to(t),cap(c),rev(r){} }; vector<edge>G[Max_v]; int level[Max_v]; //頂點到源點的距離標號 int iter[Max_v]; //當前弧,之前的邊已經用過了 void add_edge(int from,int to,int cap){ G[from].push_back(edge(to,cap,G[to].size())); G[to].push_back(edge(from,0,G[from].size()-1)); } //通過BFS構造層次圖 void bfs(int s){ memset(level,-1,sizeof(level)); queue<int>que; level[s]=0; que.push(s); while(!que.empty()){ int u=que.front();que.pop(); for(int i=0;i<G[u].size();i++){ edge e=G[u][i]; if(e.cap>0&&level[e.to]<0){ level[e.to]=level[u]+1; que.push(e.to); } } } } //在層次圖上通過dfs不斷尋找增廣路 int dfs(int u,int t,int f){ if(u==t)return f; for(int &i=iter[u];i<G[u].size();i++){ //int &i=iter[u]當前弧優化 edge &e=G[u][i]; if(e.cap>0&&level[u]<level[e.to]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return -1; } int max_flow(int s,int t){ int flow=0; while(true){ bfs(s); if(level[t]<0)return flow; memset(iter,0,sizeof(iter)); int f; while((f=dfs(s,t,inf))>0){ //兩層括號 flow+=f; } } }
3、二分圖匹配(匈牙利演算法)
描述:
思想為尋找增廣路,在二分圖匹配中,如果一條路徑首尾是非匹配點,路徑中除此之外都是匹配點,那麼這條路徑就是一條增廣路。
int V; //二分圖左側頂點數 vector<int>G[Max_v]; int match[Max_v]; //記錄妹子的男盆友 int used[Max_v]; void add_edge(int u,int v){ G[u].push_back(v); } bool dfs(int u){ used[u]=1; for(int i=0;i<G[u].size();i++){ int v=G[u][i],w=match[v]; if(w<0||!used[w]&&dfs(w)){ match[v]=u; //記錄妹子的男盆友 return true; } } return false; } int max_match(){ int ans=0; memset(match,-1,sizeof(match)); for(int i=0;i<V;i++){ memset(used,0,sizeof(used)); if(dfs(i))ans++; } return ans; }
4、最小費用流(SPFA演算法求解)
描述:
在殘餘網路上總是沿著最短(費用)路增廣。
複雜度:
< O(F|V||E|)
int V;
struct edge{
int to,cost,cap,rev;
edge(int t,int co,int c,int r):to(t),cost(co),cap(c),rev(r){}
};
int dist[Max_v];
bool used[Max_v];
int prv[Max_v],pre[Max_v]; //最短路的前驅節點和對應的邊
vector<edge>G[Max_v];
void add_edge(int from,int to,int cost,int cap){
G[from].push_back(edge(to,cost,cap,G[to].size()));
G[to].push_back(edge(from,-cost,0,G[from].size()-1));
}
ll min_cost_flow(int s,int t,int f){
ll ans=0;
while(f>0){
//用spfa尋找最短(費用)路
memset(dist,0x3f,sizeof(dist));
memset(used,0,sizeof(used));
queue<int>que;
que.push(s);
dist[s]=0;used[s]=1;
while(!que.empty()){
int u=que.front();que.pop();
used[u]=0;
for(int i=0;i<G[u].size();i++){
edge &e=G[u][i];
if(e.cap>0&&dist[e.to]>dist[u]+e.cost){
dist[e.to]=dist[u]+e.cost;
prv[e.to]=u;pre[e.to]=i;
if(!used[e.to]){
que.push(e.to);
used[e.to]=1;
}
}
}
}
if(dist[t]==inf)//找不到增廣路了
return -1;
//沿s到t的最短路儘量增廣
int d=f;
for(int v=t;v!=s;v=prv[v]){
d=min(d,G[prv[v]][pre[v]].cap);
}
f-=d;
ans+=d*dist[t];
for(int v=t;v!=s;v=prv[v]){
edge &e=G[prv[v]][pre[v]];
e.cap-=d;
G[v][e.rev].cap+=d;
}
}
return ans;
}
5、最小費用流(Dijkstra演算法求解)
描述:
在殘餘網路上總是沿著最短(費用)路增廣。引入勢h(v),取h(v)=s到v的最短距離,將邊e=(u,v)的長度變為d’(e)=d(e)+h(u)-h(v),使得對所有的e都有d’(e)>=0,就可用Dijkstra演算法求解在d’中求解最短路。
複雜度:
O(F|E|log|V|)
struct edge{
int to,cost,cap,rev; //終點、容量、費用、反向邊
edge(int t,int cp,int c,int r):to(t),cost(c0),cap(c),rev(r){}
};
int V;
int h[Max_v]; //頂點的勢
int dist[Max_v]; //考慮勢之後的最短距離
int prv[Max_v],pre[Max_v]; //最短路的前驅節點和對應的邊
vector<edge>G[Max_v];
void add_edge(int from,int to,int cost,int cap){
G[from].push_back(edge(to,cost,cap,G[to].size()));
G[to].push_back(edge(from,-cost,0,G[from].size()-1));
}
int min_cost_flow(int s,int t,int f){
int ans=0;
memset(h,0,sizeof(h));
while(f>0){
priority_queue<P,vector<P>,greater<P> >que;
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
que.push(P(0,s));
while(!que.empty()){
P p=que.top();que.pop();
int u=p.second,d=p.first;
if(d>dist[u])continue;
for(int i=0;i<G[u].size();i++){
edge &e=G[u][i];
if(e.cap>0&&dist[e.to]>dist[u]+e.cost+h[u]-h[e.to]){
dist[e.to]=dist[u]+e.cost+h[u]-h[e.to];
prv[e.to]=u;
pre[e.to]=i;
que.push(P(dist[e.to],e.to));
}
}
}
if(dist[t]==inf) //找不到增廣路了
return -1;
for(int v=0;v<V;v++)h[v]+=dist[v]; //更新h[v]
//沿s到t的最短路儘量增廣
int d=f;
for(int v=t;v!=s;v=prv[v])
d=min(d,G[prv[v]][pre[v]].cap);
f-=d;
ans+=d*h[t];
for(int v=t;v!=s;v=prv[v]){
edge &e=G[prv[v]][pre[v]];
e.cap-=d;
G[v][e.rev].cap+=d;
}
}
return ans;
}