網路流複雜度證明

班级账号發表於2024-06-16

EK(轉) dinic(轉)

EK(未完)

以此份程式碼為例

//P3376 【模板】網路最大流
//EK演算法 
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=410,M=10010;
int n,m,S,T,h[N],e[M],w[M],ne[M],idx,pre[N],dist[N],st[N],ans,g[N][N];
void add(int a,int b,int c){
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool bfs(){
	memset(st,0,sizeof st);
	queue<int> q;
	q.push(S);
	st[S]=1;
	dist[S]=(int)(1e18);
	while(q.size()){
		int t=q.front();q.pop();
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			if(!w[i]||st[j])continue;
			dist[j]=min(dist[t],w[i]);
			pre[j]=i;
			st[j]=1;
			q.push(j);
			if(j==T)return 1;
		}
	}
	return 0;
}
void update(){
	int cur=T;
	while(cur!=S){
		int i=pre[cur];
		w[i]-=dist[T];
		w[i^1]+=dist[T];
		cur=e[i^1];
	}
	ans+=dist[T];
}
signed main(){
	memset(h,-1,sizeof h);
	cin>>n>>m>>S>>T;
	for(int i=1;i<=m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		if(g[a][b])w[g[a][b]-2]+=c;
		else{
			add(a,b,c);
			add(b,a,0);
			g[a][b]=idx;
		}
	}
	while(bfs())update();
	cout<<ans;
} //by yjx

EK演算法單次bfs複雜度為 \(O(m)\)

Dinic(未完)

#include<bits/stdc++.h>
#define fir first
#define se second
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
template <typename type>
inline void read(type &x) {
	x=0; bool f=0; char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<3)+(x<<1)+(c^48);
		c=getchar();
	}
	if(f) x=-x;
}
template <typename type,typename ...t>
inline void read(type &x,t &...te) {read(x); read(te...);}
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=210,M=1e4+50;
int n,m;
int head[N],nxt[M],to[M],W[M],num=1;
int S,T;
void add(int u,int v,int w) {
	++num; nxt[num]=head[u]; to[num]=v; W[num]=w; head[u]=num;
	++num; nxt[num]=head[v]; to[num]=u; W[num]=0; head[v]=num;
}
int cur[N],dep[N];
bool bfs() {
	memset(dep,-1,sizeof(dep));
	queue<int>q;
	q.push(S);
	cur[S]=head[S];
	dep[S]=1;
	while(!q.empty()) {
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=nxt[i]) {
			int v=to[i];
			if(W[i]&&dep[v]==-1) {
				dep[v]=dep[u]+1;
				cur[v]=head[v];
				if(v==T) return true;
				q.push(v);
			}
		}
	}
	return false;
}
ll dfs(int u,ll lim) {
	ll flow=0;
	if(u==T) return lim;
	for(int i=cur[u];i&&flow<lim;i=nxt[i]) {
		int v=to[i];
		cur[u]=i;
		if(W[i]&&dep[v]==dep[u]+1) {
			ll t=dfs(v,min(lim-flow,W[i]));
			if(!t) dep[v]=-1;
			W[i]-=t;
			W[i^1]+=t;
			flow+=t;
		}
	}
	return flow;
}
int dinic() {
	int ans=0,flow;
	while(bfs()) 
		while(flow=dfs(S,0x3f3f3f3f)) 
			ans+=flow;
	return ans;
}
signed main() {
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	read(n,m,S,T);
	for(int i=1;i<=m;i++) {
		int u,v,w;
		read(u,v,w);
		add(u,v,w);
	}
	printf("%lld",dinic());
	return 0;
}

Dinic演算法單次bfs複雜度為 \(\text{O}(m)\)

增廣輪數<=n

單輪增廣複雜度 \(\text{O}(nm)\)

下面我們來證明dinic增廣輪數<=n

首先dinic會按照層數分層,將長度最短的增廣路徑全部刪除,因此進行一遍dfs後,S到T在殘量網路上的距離將會改變。

現在我們證明每一次S到T在殘量網路上的距離將會增加1

網路流複雜度證明

設此時S到u經過若干條邊後聯通,S經過若干條邊與v聯通且dis(S,v)>dis(S,u)距離(不聯通也是相同道理,相當於在不經過u的情況下S到v的距離為INF),v經過若干條邊與T聯通,且S->u->v->T是最短的一條增廣路徑,而u->v是增廣路徑上邊權最小的邊。

經過一遍增廣後,此時殘量網路會變成這樣

網路流複雜度證明

此時S->u->v->T不聯通,而dis(S,v)>dis(S,u),因此新增v->u邊並不會讓S到T的距離更小。

因此只會讓S->T距離增大,且至少增加1。又因為dis(S,T)最大為n,因此增廣輪數最多為n。

下面我們來證單輪增廣複雜度 \(\text{O}(nm)\)

首先每次推流會清空至少一條邊u->v,而此時新增的反向邊不滿足dep[v]==dep[u]+1,因此最多清空 \(\text{O}(m)\) 次,對於清空每一條邊需要找到一條他所對應的增廣路,此時最多從S訪問到T,因此總複雜度為 \(\text{O}(mn)\)

因此Dinic複雜度為 \(O(n^2m)\)

(從複雜度分析就可很顯然地看出,Dinic複雜度在正常題目建圖情況下達不到該上界,因此總是跑得飛快)

by lyk

借鑑了[該地址](Dinic演算法複雜度證明 | yukiyama (iyukiyama.github.io))內容

相關文章