1. 程式人生 > 實用技巧 >網路最大流Dinic

網路最大流Dinic

1.什麼是網路最大流

形象的來說,網路最大流其實就是這樣一個生活化的問題:現在有一個由許多水管組成的水流系統,每一根管道都有自己的最大通過水流限制(流量),

超過這個限制水管會爆(你麻麻就會來找你喝茶qwq)。現在,給定你一個出水口(原點),一個出水口(匯點),求這個網路中水流量的最大值。

????看起來很簡單對不對?在我們看起來的確是這樣的,而這部分的難點也確實不在思路上,而是在於演算法設計以及程式碼實現上。

2.怎麼求解網路最大流

首先想明白一件事情,對於一個節點來說,他接受的流量一定小於等於他給出的流量之和,否則,水管一定會爆掉。而對於一個節點來說,

他接受的流量有可能大於任意一個他出邊的流量,因為這個節點可以把接受流給出到不同的水管上,進而實現分流。

有了這兩點,思路就很清晰了(貪心算):

1.首先,我們需要尋找一條可行的流量方案(此時,不一定為最大流量)。

2.然後我們依次擴充套件這條路徑上的所有節點,看看這個節點是否還可以接受流量,直到已經滿流。

3.重複上述步驟,直到沒有可行流動路徑。

4.此時我們累加的流量即為網路最大流,我們把這種方法稱為最大流Dinic演算法

3.實現細節

這種演算法看起來簡單,實際上實現起來會遇到許多小毛病,以及許多很難理解的程式碼實現,這裡舉一個栗子

在步驟2的時候我們採用dfs進行擴充套件,也稱為網路最大流的擴充套件部分演算法,需要藉助到反邊這樣一個概念,即:兩個節點A,B間有一條權值為w無向邊

我們就把他拆分成一條由A指向B的有向邊與一條由B指向A的有向邊,其中,這兩條邊的權值之和為w,這樣一來一回,兩者相互抵消巧妙的實現了回溯

上程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 1000005
#define M 4*1000005
#define INF 0xfffffff

using namespace std;

int Read()//快讀
{
	int num=0,k=1;
	char c=getchar();
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	if(c=='-')
	{
		k=-1;
		c=getchar();
	}
	while(c<='9'&&c>='0')
	{
		num=(num<<3)+(num<<1)+c-'0';
		c=getchar();
	}
	return num*k;
}

struct node
{
    int from;
	int to;
	int v;
	int next;	
};

node edge[2*M];
int cnt_edge=1,n,m,s,t;
long long ans=0;
int last[N],deep[N];

void add_edge(int u,int v,int w)
{
	edge[++cnt_edge].from=u;
	edge[cnt_edge].to=v;
	edge[cnt_edge].v=w;
	edge[cnt_edge].next=last[u];
	last[u]=cnt_edge;
}

bool bfs()  //判斷是否有通路
{
	memset(deep,-1,sizeof(deep));
	deep[s]=0;
	queue<int >q;
	q.push(s);
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=last[now];i;i=edge[i].next)
		{
			int j=edge[i].to;
			if(deep[j]==-1&&edge[i].v)
			{
				deep[j]=deep[now]+1;
				q.push(j);
			}
		}
	}
	return deep[t]!=-1;
}

int dfs(int now,int flow)  //flow為當前流量
{
	if(now==t) return flow;
	int delta=flow;    //delta是剩餘流量,就是流不下去的流量
	 
	for(int i=last[now];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if((deep[to]==(deep[now]+1))&&edge[i].v > 0)
		{
			int d=dfs(to,min(delta,edge[i].v));
			if(!d) deep[to] = 1e9; //剪枝優化,當前點無法下流 
			edge[i].v-=d;edge[i^1].v+=d;delta-=d;//流下去,反邊+d,方便迴流 
			
			if(!delta) break;
		}
	}
	return flow-delta; //返回這裡留下去了多少 ,即當前點的最大流量 
 } 

int main ()
{
	n=Read();m=Read();s=Read();t=Read();
	
	int u,v,w;
	
	for(int i=1;i<=m;i++)
	{
		u=Read();v=Read();w=Read();
		add_edge(u,v,w);add_edge(v,u,0);
	}
	
	while(bfs())   ans+=dfs(s,INF);
	
	printf("%lld\n",ans);
	return 0;
}