【ybt金牌导航3-2-1】【luogu P3376】网络最大流【Dinic算法】

网 络 最 大 流 网络最大流

题目链接:ybt金牌导航3-2-1 / luogu P3376

题目

如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

输入

第一行包含四个正整数 N N N M M M S S S T T T,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来 M M M行每行包含三个正整数 u i ui ui v i vi vi w i wi wi,表示第i条有向边从 u i ui ui出发,到达 v i vi vi,边权为 w i wi wi(即该边最大流量为 w i wi wi

输出

一行,包含一个正整数,即为该网络的最大流。

样例输入

4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40

样例输出

50

样例解释

在这里插入图片描述
题目中存在 3 3 3条路径:

  1. 路径为: 4 − − > 2 − − > 3 4-->2-->3 4>2>3,该路线可通过 20 20 20的流量
  2. 路径为: 4 − − > 3 4-->3 4>3,可通过 20 20 20的流量
  3. 路径为: 4 − − > 2 − − > 1 − − > 3 4-->2-->1-->3 4>2>1>3,可通过 10 10 10的流量(边 4 − − > 2 4-->2 4>2之前已经耗费了 20 20 20的流量)

故流量总计 20 + 20 + 10 = 504 20+20+10=504 20+20+10=504。输出 50 50 50

数据范围

对于 30 % 30\% 30%的数据: N < = 10 N<=10 N<=10 M < = 25 M<=25 M<=25
对于 70 % 70\% 70%的数据: N < = 200 N<=200 N<=200 M < = 1000 M<=1000 M<=1000
对于 100 % 100\% 100%的数据: N < = 10000 N<=10000 N<=10000 M < = 100000 M<=100000 M<=100000

思路

这是一道非常经典的模板题,我们这里用 D i n i c Dinic Dinic算法来做。

其实就是把 F o r d − F u l k e r s o n Ford-Fulkerson FordFulkerson算法的找增广路径和 d f s dfs dfs增广分开来,并在 d f s dfs dfs时进行了优化。

那优化是怎么样的呢?
我们在 b f s bfs bfs找增广路径的时候,我们可以将记录这个点是否被走过的 b o o l bool bool数组改成 i n t int int。这样我们除了表示它有没有被走过,还可以表示由源点到这个点走了多远。
接着,我们在 d f s dfs dfs增广的时候,我们就在某一个点的时候,我们就只找这个值刚好比他大 1 1 1的点走。这样,走出我们的增广路就快了很多了。
(我这里记录的变量名叫 d i s dis dis

这样,我们就实现了优化。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define rr register

using namespace std;

struct note {
	int to, go, op, next;
}e[200001];
int n, m, s, t, le[200001], x, y, z, k, dis[10001], ans;
queue <int> q;

int read() {//快读
	int aa = 0;
	char c = getchar();
	while (c > '9' || c < '0') c = getchar();
	while (c <= '9' && c >= '0') {
		aa = aa * 10 + c - 48;
		c = getchar();
	}
	return aa;
}

bool bfs() {
	while (!q.empty()) q.pop();//清空队列
	memset(dis, 0x7f, sizeof(dis));//初始化
	dis[s] = 0;//初始化
	
	q.push(s);
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		
		for (rr int i = le[now]; i; i = e[i].next)
			if (dis[e[i].to] > dis[now] + 1 && e[i].go) {//没到过且可以灌水
				dis[e[i].to] = dis[now] + 1;
				if (e[i].to == t) return 1;//到汇点了
				q.push(e[i].to);
			}
	}
	
	return 0;
}

int dfs(int now, int an) {
	if (now == t) return an;//到汇点了
	
	int go = 0;
	for (rr int i = le[now]; i; i = e[i].next)
		if (dis[e[i].to] == dis[now] + 1 && e[i].go) {
			int line_go = dfs(e[i].to, min(e[i].go, an - go));//这条增广路最多能流多少
			if (!line_go) dis[e[i].to] = -1;//之前找到的不是这条路
			e[i].go -= line_go;//流
			e[e[i].op].go += line_go;//反向记录
			
			go += line_go;//总流量
			if (go == an) break;//已经全部找完了
		}
	
	return go;
}

void dinic() {
	while (bfs())//还有增广路径
		ans += dfs(s, 2147483647);//dfs加最大流
}

int main() {
	n = read();//读入
	m = read();
	s = read();
	t = read();
	
	for (rr int i = 1; i <= m; i++) {
		x = read();//读入
		y = read();
		z = read();
		e[++k] = (note){y, z, k + 1, le[x]}; le[x] = k;//建邻接表(正向)
		e[++k] = (note){x, 0, k - 1, le[y]}; le[y] = k;//建邻接表(反向)
	}
	
	dinic();//dinic算法
	
	printf("%d", ans);//输出
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值