算法筆記:Dinic最大流和SPFA費用流

dinic求最大流 複雜度m*n^2

個人對反向邊的存在的理解:當沒有流到最優路線的時候,給一個反悔的機會

增廣路:從s到t的一條路徑 正向邊流量全部不滿capacity 反向邊的流量全部>0,跑了可以使最大容量增加,dinic就是增廣路算法

引用luogu上的大佬的dinic算法的過程:
1.先利用BFS對殘餘網絡分層 一個節點的深度,就是源點到它最少要經過的邊數。
在這裏插入圖片描述

2.利用BFS對殘餘網絡分層,分完層後,利用DFS從前一層向後一層反覆尋找增廣路。
在這裏插入圖片描述

3.分完層後,從源點開始,用DFS從前一層向後一層反覆尋找增廣路(即要求DFS的每一步都必須要走到下一層的節點)。 因此,前面在分層時,只要進行到匯點的層次數被算出即可停止,因爲按照該DFS的規則,和匯點同層或更下一層的節點,是不可能走到匯點的。

4.DFS過程中,要是碰到了匯點,則說明找到了一條增廣路徑。此時要增加總流量的值,消減路徑上各邊的容量,並添加反向邊,即所謂的進行增廣。

5.DFS找到一條增廣路徑後,並不立即結束,而是回溯後繼續DFS尋找下一個增廣路徑。 回溯到哪個節點呢? 回溯到的節點u滿足以下條件:

6.DFS搜索樹的樹邊(u,v)上的容量已經變成0。即剛剛找到的增廣路徑上所增加的流量,等於(u,v)本次增廣前的容量。(DFS的過程中,是從u走到更下層的v的)
u是滿足條件 1)的最上層的節點如果回溯到源點而且無法繼續往下走了,DFS結束。 因此,一次DFS過程中,可以找到多條增廣路徑。

7.DFS結束後,對殘餘網絡再次進行分層,然後再進行DFS當殘餘網絡的分層操作無法算出匯點的層次(即BFS到達不了匯點)時,算法結束,最大流求出。

luogu P3376 【模板】網絡最大流

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
const int inf=0x3f3f3f3f;
int dep[maxn],inque[maxn];
int top=1;
int maxflow,n,m,s,t;
struct edge{
	int v,w,rev;
};
vector<edge>e[2*maxm];
inline void add_edge(int u,int v,int w){
	e[u].push_back((edge){v,w,e[v].size()});
	e[v].push_back((edge){u,0,e[u].size()-1});
}
bool bfs(){
	memset(dep,inf,sizeof(dep));
	memset(inque,0,sizeof(inque));
	dep[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		inque[u]=0;
		for(int i=0;i<e[u].size();i++){
			int v=e[u][i].v;
			edge x=e[u][i];
			if(dep[v]>dep[u]+1&&x.w){
				dep[v]=dep[u]+1;
				if(inque[v]==0){
					q.push(v);
					inque[v]=1;
				}
			}
		}
	}
	if(dep[t]!=inf)return true;
	return false;
}
int dfs(int u,int flow){
	int rlow=0;
	if(u==t)return flow;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i].v;
		if(e[u][i].w&&dep[v]==dep[u]+1){
			if(rlow=dfs(v,min(flow,e[u][i].w))){
				e[u][i].w-=rlow;
				e[v][e[u][i].rev].w+=rlow;
				return rlow;
			}
		}
	}
	return 0;
}
int dinic(){
	int lowflow;
	while(bfs()){
		while(lowflow=dfs(s,inf))maxflow+=lowflow;
	}
	return maxflow;
}
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	int u,v,w;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&u,&v,&w);
		add_edge(u,v,w);
	}
	printf("%d",dinic());
	return 0;
}

最小費用最大流的思路其實跟最大流差不多,用spfa代替bfs,再跑dfs進行增廣,同時累加最大流
把dfs中的dep[v]==dep[u]+1改成dis[v]==dis[u]+c(c是這條邊的費用),用一個數組used記錄該點用了的流量

時間複雜度nmf

vis數組來避免存在費用爲0的邊讓程序陷入死循環

luogu P3381 【模板】最小費用最大流

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=5e3+5,maxm=5e4+5;
int n,m,s,t,cost,maxflow,vis[maxn],dis[maxn];
struct edge{
	int v,flow,cost,rev;
};
vector<edge>e[maxm*2];
inline void add_edge(int u,int v,int flow,int cost){
	e[u].push_back((edge){v,flow,cost,e[v].size()});
	e[v].push_back((edge){u,0,-cost,e[u].size()-1});
}
bool spfa(){
	memset(vis,0,sizeof(vis));
	memset(dis,inf,sizeof(dis));
	dis[s]=0;
	vis[s]=1;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=0;i<e[u].size();i++){
			int v=e[u][i].v;
			int c=e[u][i].cost;
			if(dis[v]>dis[u]+c&&e[u][i].flow){
				dis[v]=dis[u]+c;
				if(vis[v]==0){
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
	if(dis[t]!=inf)return true;
	return false;
}
int dfs(int u,int flow){
	if(u==t){
		vis[t]=1;
		maxflow+=flow;
		return flow;
	}
	int used=0;
	vis[u]=1;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i].v;
		int c=e[u][i].cost;
		if((vis[v]==0||v==t)&&e[u][i].flow!=0&&dis[v]==dis[u]+c){
			int minflow=dfs(v,min(flow-used,e[u][i].flow));
			if(minflow!=0){
				cost+=c*minflow;
				e[u][i].flow-=minflow;
				e[v][e[u][i].rev].flow+=minflow;
				used+=minflow;
				if(used==flow)break;
			}
		}
	}
	return used;
}
int mcmf(){
	while(spfa()){
		vis[t]=1;
		while(vis[t]){
			memset(vis,0,sizeof(vis));
			dfs(s,inf);
		}
	}
	return maxflow;
}
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	int u,v,f,w;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d%d",&u,&v,&f,&w);
	    add_edge(u,v,f,w);
	}
	mcmf();
	printf("%d %d",maxflow,cost);
	return 0;
}

網絡流的算法其實挺少(主動無視FF EK 那些不夠高效的算法和ISAP HLPP這種卡極致複雜度的算法)

難點主要在於建模,慢慢練習積累建圖經驗。

立個flag(2020年結束之前a掉50題網絡流&圖匹配) (逃

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章