templete_Dinic(最短增广路算法)

Dinic算法通过层次划分优化搜索过程,利用BFS确定节点层次,确保每次寻找增广路时不重复访问节点,从而减少盲目搜索。在代码中已注明关键细节,欢迎交流指导,共同提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Dinic算法的算法是这样的:将整个网络划分成不同的层次,应该可以理解,用BFS且队列维护的方法一次访问的结点,并且标上他们的层次,这样的访问是最小的路径,可以不用来回的访问结点,这样就极大的减小了盲目的搜索,这就成了走最少的结点,也能找出增广路,这样不省时间,怎么样省呢?

其他的注意事项我都在代码中写了,大家看看,有需要指点的,请指明共同进步:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <queue>
#include <string>

using namespace std;

const int MAXN = 100 + 11;

const int INF = 0x3fffffff;

struct Edge
{
	int from, to;
	int cap, flow; //注意这里的顺序是不能随便改变的,防止你下面插入边的时候顺序不同,就出大错了。 
}edges;

struct Dinic
{
	int N, M;
	int s, t;
	vector <Edge> edges;
	vector <int> G[MAXN];
	bool vis[MAXN];
	int d[MAXN];
	int cur[MAXN];
	
	void init(int n, int m)
	{
		this->N = n;
		this->M = m;
	}
	
	void clearAll()
	{
		edges.clear();
		for (int i = 0; i < N; i++)
		{
			G[i].clear();
		}
	}
	
	void addEdge(int a, int b, int c) //建立边 
	{
		edges.push_back((Edge){a, b, c, 0});
		edges.push_back((Edge){b, a, 0, 0});
		int nc = edges.size();
		G[a].push_back(nc - 2); //这样的建立关系的话,就存在着这样的关系:反向的两条边存在edges[e],edges[e ^ 1]互为反向边 
		G[b].push_back(nc - 1); 
	}
	
	bool BFS() //BFS将所有的点分割成层次模型 
	{
		memset(vis, 0, sizeof(vis));
		queue <int> Q;
		Q.push(s);
		vis[s] = 1;
		while (!Q.empty())
		{
			int u = Q.front();
			Q.pop();
			int nc = G[u].size();
			for (int i = 0; i < nc; i++)
			{
				Edge& e = edges[G[u][i]];
				int v = e.to;
				if (!vis[v] && e.cap > e.flow)
				{
					vis[v] = 1;
					d[v] = d[u] + 1;
					Q.push(v);
				}
				
			}
		}
		return vis[t]; //看看层次网络能不能访问到汇点,如果访问不到,就不用再继续做了。 
	}
	
	int DFS(int u, int a)
	{
		if (u == t || a == 0) //当访问到最后的汇点的时候,或者前面流过来的流量为0,那么就不用做了,直接返回前面的流量就是了。 
		{
			return a;
		}
		int nc = G[u].size();
		int flow = 0;
		int f;
		for (int& i = cur[u]; i < nc; i++) //这里的cur[x]我确实不知道有什么高明的用处,就是防止访问的时候再返回来访问父节点。 
		{
			Edge& e = edges[G[u][i]];
			int v = e.to;
			if (d[v] == d[u] + 1 && (f = DFS(v, min(a, e.cap - e.flow))) > 0) //这里先判断是不是访问下一层次的,如果是的,继续判断最大流是否存在 
			{
				e.flow += f;
				edges[G[u][i] ^ 1].flow -= f; //建边的时候,我才看懂。为什么要-f,因为你设置的反向流量的cap为0,所以这样做正好符合残留网络! 
				a -= f;						//而且也和出反向边的目的:为了更好的反悔,相悖呀。不怎么理解。 
				flow += f;
				if (a == 0) //这个判断是非常的好的,a == 0的时候就是说明了,前面传过来的最小容量,我访问到这几个边的时候 
				{			//就已经用完了,后面的边,我不用访问了,因为访问我也用不到了呀。所以直接退出循环。 
					break;
				}
			}
		}
		return flow;
	}
	
	int maxFlow(int s, int t)
	{
		this->s = s;
		this->t = t;
		int flow = 0;
		while (BFS())//每次都做BFS来判断残留网络中的层次模型是否能够到达汇点 
		{
			memset(cur, 0, sizeof(cur)); //避免访问到父节点。 
			flow += DFS(s, INF);
		}
		return flow;
	} 
};

Dinic dn;

int main()
{
	int N, M;
	while (scanf("%d%d", &N, &M) != EOF)
	{
		dn.init(N, M);
		dn.clearAll();
		int a, b, c;
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &a, &b, &c);
			dn.addEdge(a, b, c);
		}
		int s, t;
		scanf("%d%d", &s, &t);
		int ans = dn.maxFlow(s, t);
		printf("%d\n", ans);
	}
	system("pause");
	return 0;
}


/*

6 8
1 2 3
1 3 4
2 4 1
2 5 5
3 5 5
4 6 2
5 6 6
3 6 4
1 6

4 5
1 2 1
2 3 1
1 3 1
2 4 1
3 4 1
1 4

*/ 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值