Dinic算法JAVA模板

笔者学习来源:https://www.cnblogs.com/SYCstudio/p/7260613.html

    在这个信息爆炸的时代,本着达者为师的态度在大佬的博客中取的Dinic模板之经。

   为在下以后愉快的玩耍板子,写写学习经验总坛。

Dinic算法

   相较于EK算法,dinic多了一个分层的概念;

   dinic做法,首先用bfs把图分层,分深度,即每个节点都有他的上线层和下线层,只有他的(直接)上线才能到达他,其他不能直接到达,这用于dfs时的高效性;

 分层之后就是dfs跑图,寻找流然后,以递归的形式更新所在的边;

dfs这里的优化是一个pre数组(本代码中的),每当找到一个可行流时dfs就结束了,然后再dfs寻找其他可行流,

这其中会有很多比如这条边遍历过,并由此找到过可行流,如果还往这条边下降搜索就没意义了,应为由这条边去的没可行流了(已经被更新了)pre数组就是跳过这条边;

 dfs直到dfs不到新的可行流时,再bfs重新分层判断是否有路,然后dfs,反复操作之下直到bfs不到还有路为止;

  对于一张图来说,如果该边是单向的话,初始时它的反边权值一定为零;无向则权值相等;

对此算法不是很了解(弱弱),可看开始推荐的大佬博客加深理解

以下代码仅够参考,如发现错误,请指出 w(゚Д゚)w


import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	public static class edg{
		private int to;
		private int next;
		private int v;
		
		public edg(){
			
		}
		public edg(int to,int next,int v){
			this.next=next;
			this.to=to;
			this.v=v;
		}
	}
	static int head[],cent=0;
	static edg a[];
	static boolean vis[];
	static int pre[];     //记录数组
	static int s,t,n;       //起点,终点;
	static int deep[];     //记录深度,用于分层
	public static void main(String[] args) {
	Scanner sc=new Scanner(System.in);
	 n=sc.nextInt();
	int m=sc.nextInt();
    a=new edg[m+1];
    pre=new int[n+1];
    head=new int[n+1];
    s=1;t=n;
    for (int i = 0; i <=n; i++) {
		head[i]=-1;
	}
    for (int i = 0; i <m; i++) {   //建图
		int x=sc.nextInt();
		int y=sc.nextInt();
		int z=sc.nextInt();
		add(x,y,z);
		add(y,x,0);
	}
    System.out.println(Dinic());  //寻找最大流
    

}	
	public static int Dinic(){  //
		int ans=0;
		while(bfs()){               //判断是否还存在增广路还有重要的分层;
			for (int i = 1; i <=n; i++) {     //重置我们的记录每个点遍历到哪条边用的数组
				pre[i]=head[i];
			}
			int d=-1;
			while(d!=0){ 
				d=dfs(s,Integer.MAX_VALUE);  //寻找可增广流量
				ans+=d;                      //累加即可
			}
		}
	return ans;         //返回即可
	}
	public static boolean bfs(){
		Queue<Integer> qp=new LinkedList<Integer>();   //队列
		deep=new int[n+1];                             //用于分层  deep数组初始化为0
		deep[s]=1;                                     
		qp.offer(s);
		do{
			int v=qp.poll();
			for (int i =head[v]; i!=-1; i=a[i].next) {
				int k=a[i].to;
				if(deep[k]==0&&a[i].v!=0){            //若该残量不为0,且V[i]还未分配深度,则给其分配深度并放入队列
					deep[k]=deep[v]+1;
					qp.offer(k);
				}
			}
		}while(!qp.isEmpty());
		if(deep[t]==0){                    //如果为0,就说明没有增广路到t了
			return false;
		}else{
			return true;
		}
		return false; //非常重要
	}
	public static int dfs(int u,int flow){
		if(u==t){                           //dfs到汇点直接返回这个
			return flow;
		}
		for (int i =pre[u]; i!=-1;i=a[i].next) {  //pre数组可以减少重复搜索的边,相当于跳过已经遍历的边,
			int k=a[i].to;pre[u]=i;
			if(deep[k]==deep[u]+1&&a[i].v!=0){
				int d=dfs(k,Math.min(flow,a[i].v));// folw 代表的是通过递归的形式把该增广路的最大可增广流量求出来
				if(d>0){                            //即该路所以边的最大可行流的最小值,大家都可以流的量
					a[i].v-=d; // 然后就是更新边了
					a[i^1].v+=d;  //流的边减,流的反边加;
					return d;      //递归返回 上层边更新
				}
			}
		}
		return 0;
	}
	public static void add(int x,int y,int z){
		a[cent]=new edg(y,head[x],z);
		head[x]=cent++;
	}
	
}
public static int dfs(int u,int flow) {
		if(u==t)return flow;
		int fw=0,used=0;
		for (int i =head[u]; i!=-1; i=a[i].next) {
			int v=a[i].to;
			if(a[i].v>0&&deep[v]==deep[u]+1) {
				fw=dfs(v,Math.min(a[i].v, flow-used));
				if(fw>0) {
					a[i].v-=fw;a[i^1].v+=fw;
					used+=fw;if(used==flow)break;
				}
			}
		}
		return used;
	}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值