zoukankan      html  css  js  c++  java
  • 网络最大流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
    ",ans);
    	return 0;
    }
    

      看完关注哦~

  • 相关阅读:
    C语言实验报告
    C语言实验报告
    第四次作业4-树和二叉树
    第03次作业-栈和队列
    第02次作业-线性表
    Data_Structure01-绪论作业
    C语言第二次实验报告
    C语言实验报告
    第04次作业-树
    第03次作业-栈和队列
  • 原文地址:https://www.cnblogs.com/Roysblog/p/13777368.html
Copyright © 2011-2022 走看看