网络最大流Dinic

   1.什么是网络最大流

    形象的来说,网络最大流其实就是这样一个生活化的问题:现在有一个由许多水管组成的水流系统,每一根管道都有自己的最大通过水流限制(流量),超过这个限制水管会爆(你麻麻就会来找你喝茶qwq)。现在,给定你一个出水口(原点),一个出水口(汇点),求这个网络中水流量的最大值。

    ????看起来很简单对不对?在我们看起来的确是这样的,而这部分的难点也确实不在思路上,而是在于算法设计以及代码实现上。

    2.怎么求解网络最大流

    首先想明白一件事情,对于一个节点来说,他接受的流量一定小于等于他给出的流量之和,否则,水管一定会爆掉。而对于一个节点来说,他接受的流量有可能大于任意一个他出边的流量,因为这个节点可以把接受流给出到不同的水管上,进而实现分流。

有了这两点,思路就很清晰了(贪心算):

       1.首先,我们需要寻找一条可行的流量方案(此时,不一定为最大流量)。

       2.然后我们依次扩展这条路径上的所有节点,看看这个节点是否还可以接受流量,直到已经满流。

       3.重复上述步骤,直到没有可行流动路径。

       4.此时我们累加的流量即为网络最大流,我们把这种方法称为最大流Dinic算法

  3.实现细节

  这种算法看起来简单,实际上实现起来会遇到许多小毛病,以及许多很难理解的代码实现,这里举一个栗子

  在步骤2的时候我们采用dfs进行扩展,也称为网络最大流的扩展部分算法,需要借助到反边这样一个概念,即:两个节点A,B间有一条权值为w无向边。我们就把他拆分成一条由A指向B的有向边与一条由B指向A的有向边,其中,这两条边的权值之和为w,这样一来一回,两者相互抵消巧妙的实现了回溯

   上代码:qwq

#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;
}

  看完关注哦~

posted @ 2020-10-07 14:53  Roy0_0  阅读(222)  评论(0编辑  收藏  举报
Live2D