笔者学习来源: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;
}