当前位置: 首页 > 编程日记 > 正文

C/C++,图算法——Dinic最大流量算法

1 文本格式

// C++ implementation of Dinic's Algorithm 
#include<bits/stdc++.h> 
using namespace std;

// A structure to represent a edge between 
// two vertex 
struct Edge
{
    int v;  // Vertex v (or "to" vertex) 
    // of a directed edge u-v. "From" 
    // vertex u can be obtained using 
    // index in adjacent array.

int flow; // flow of data in edge

int C;    // capacity

int rev; // To store index of reverse 
    // edge in adjacency list so that 
    // we can quickly find it. 
};

// Residual Graph 
class Graph
{
    int V; // number of vertex 
    int* level; // stores level of a node 
    vector< Edge >* adj;
public:
    Graph(int V)
    {
        adj = new vector<Edge>[V];
        this->V = V;
        level = new int[V];
    }

// add edge to the graph 
    void addEdge(int u, int v, int C)
    {
        // Forward edge : 0 flow and C capacity 
        Edge a{ v, 0, C, adj[v].size() };

// Back edge : 0 flow and 0 capacity 
        Edge b{ u, 0, 0, adj[u].size() };

adj[u].push_back(a);
        adj[v].push_back(b); // reverse edge 
    }

bool BFS(int s, int t);
    int sendFlow(int s, int flow, int t, int ptr[]);
    int DinicMaxflow(int s, int t);
};

// Finds if more flow can be sent from s to t. 
// Also assigns levels to nodes. 
bool Graph::BFS(int s, int t)
{
    for (int i = 0; i < V; i++)
        level[i] = -1;

level[s] = 0;  // Level of source vertex

// Create a queue, enqueue source vertex 
    // and mark source vertex as visited here 
    // level[] array works as visited array also. 
    list< int > q;
    q.push_back(s);

vector<Edge>::iterator i;
    while (!q.empty())
    {
        int u = q.front();
        q.pop_front();
        for (i = adj[u].begin(); i != adj[u].end(); i++)
        {
            Edge& e = *i;
            if (level[e.v] < 0 && e.flow < e.C)
            {
                // Level of current vertex is, 
                // level of parent + 1 
                level[e.v] = level[u] + 1;

q.push_back(e.v);
            }
        }
    }

// IF we can not reach to the sink we 
    // return false else true 
    return level[t] < 0 ? false : true;
}

// A DFS based function to send flow after BFS has 
// figured out that there is a possible flow and 
// constructed levels. This function called multiple 
// times for a single call of BFS. 
// flow : Current flow send by parent function call 
// start[] : To keep track of next edge to be explored. 
//           start[i] stores  count of edges explored 
//           from i. 
//  u : Current vertex 
//  t : Sink 
int Graph::sendFlow(int u, int flow, int t, int start[])
{
    // Sink reached 
    if (u == t)
        return flow;

// Traverse all adjacent edges one -by - one. 
    for (; start[u] < adj[u].size(); start[u]++)
    {
        // Pick next edge from adjacency list of u 
        Edge& e = adj[u][start[u]];

if (level[e.v] == level[u] + 1 && e.flow < e.C)
        {
            // find minimum flow from u to t 
            int curr_flow = min(flow, e.C - e.flow);

int temp_flow = sendFlow(e.v, curr_flow, t, start);

// flow is greater than zero 
            if (temp_flow > 0)
            {
                // add flow  to current edge 
                e.flow += temp_flow;

// subtract flow from reverse edge 
                // of current edge 
                adj[e.v][e.rev].flow -= temp_flow;
                return temp_flow;
            }
        }
    }

return 0;
}

// Returns maximum flow in graph 
int Graph::DinicMaxflow(int s, int t)
{
    // Corner case 
    if (s == t)
        return -1;

int total = 0;  // Initialize result

// Augment the flow while there is path 
    // from source to sink 
    while (BFS(s, t) == true)
    {
        // store how many edges are visited 
        // from V { 0 to V } 
        int* start = new int[V + 1];

// while flow is not zero in graph from S to D 
        while (int flow = sendFlow(s, INT_MAX, t, start))

// Add path flow to overall flow 
            total += flow;
    }

// return maximum flow 
    return total;
}

// Driver program to test above functions 
int main()
{
    Graph g(6);
    g.addEdge(0, 1, 16);
    g.addEdge(0, 2, 13);
    g.addEdge(1, 2, 10);
    g.addEdge(1, 3, 12);
    g.addEdge(2, 1, 4);
    g.addEdge(2, 4, 14);
    g.addEdge(3, 2, 9);
    g.addEdge(3, 5, 20);
    g.addEdge(4, 3, 7);
    g.addEdge(4, 5, 4);

// next exmp 
    /*g.addEdge(0, 1, 3 );
      g.addEdge(0, 2, 7 ) ;
      g.addEdge(1, 3, 9);
      g.addEdge(1, 4, 9 );
      g.addEdge(2, 1, 9 );
      g.addEdge(2, 4, 9);
      g.addEdge(2, 5, 4);
      g.addEdge(3, 5, 3);
      g.addEdge(4, 5, 7 );
      g.addEdge(0, 4, 10);

// next exp
     g.addEdge(0, 1, 10);
     g.addEdge(0, 2, 10);
     g.addEdge(1, 3, 4 );
     g.addEdge(1, 4, 8 );
     g.addEdge(1, 2, 2 );
     g.addEdge(2, 4, 9 );
     g.addEdge(3, 5, 10 );
     g.addEdge(4, 3, 6 );
     g.addEdge(4, 5, 10 ); */

cout << "Maximum flow " << g.DinicMaxflow(0, 5);
    return 0;
}

2 代码格式

// C++ implementation of Dinic's Algorithm 
#include<bits/stdc++.h> 
using namespace std;

// A structure to represent a edge between 
// two vertex 
struct Edge
{
	int v;  // Vertex v (or "to" vertex) 
	// of a directed edge u-v. "From" 
	// vertex u can be obtained using 
	// index in adjacent array. 

	int flow; // flow of data in edge 

	int C;    // capacity 

	int rev; // To store index of reverse 
	// edge in adjacency list so that 
	// we can quickly find it. 
};

// Residual Graph 
class Graph
{
	int V; // number of vertex 
	int* level; // stores level of a node 
	vector< Edge >* adj;
public:
	Graph(int V)
	{
		adj = new vector<Edge>[V];
		this->V = V;
		level = new int[V];
	}

	// add edge to the graph 
	void addEdge(int u, int v, int C)
	{
		// Forward edge : 0 flow and C capacity 
		Edge a{ v, 0, C, adj[v].size() };

		// Back edge : 0 flow and 0 capacity 
		Edge b{ u, 0, 0, adj[u].size() };

		adj[u].push_back(a);
		adj[v].push_back(b); // reverse edge 
	}

	bool BFS(int s, int t);
	int sendFlow(int s, int flow, int t, int ptr[]);
	int DinicMaxflow(int s, int t);
};

// Finds if more flow can be sent from s to t. 
// Also assigns levels to nodes. 
bool Graph::BFS(int s, int t)
{
	for (int i = 0; i < V; i++)
		level[i] = -1;

	level[s] = 0;  // Level of source vertex 

	// Create a queue, enqueue source vertex 
	// and mark source vertex as visited here 
	// level[] array works as visited array also. 
	list< int > q;
	q.push_back(s);

	vector<Edge>::iterator i;
	while (!q.empty())
	{
		int u = q.front();
		q.pop_front();
		for (i = adj[u].begin(); i != adj[u].end(); i++)
		{
			Edge& e = *i;
			if (level[e.v] < 0 && e.flow < e.C)
			{
				// Level of current vertex is, 
				// level of parent + 1 
				level[e.v] = level[u] + 1;

				q.push_back(e.v);
			}
		}
	}

	// IF we can not reach to the sink we 
	// return false else true 
	return level[t] < 0 ? false : true;
}

// A DFS based function to send flow after BFS has 
// figured out that there is a possible flow and 
// constructed levels. This function called multiple 
// times for a single call of BFS. 
// flow : Current flow send by parent function call 
// start[] : To keep track of next edge to be explored. 
//           start[i] stores  count of edges explored 
//           from i. 
//  u : Current vertex 
//  t : Sink 
int Graph::sendFlow(int u, int flow, int t, int start[])
{
	// Sink reached 
	if (u == t)
		return flow;

	// Traverse all adjacent edges one -by - one. 
	for (; start[u] < adj[u].size(); start[u]++)
	{
		// Pick next edge from adjacency list of u 
		Edge& e = adj[u][start[u]];

		if (level[e.v] == level[u] + 1 && e.flow < e.C)
		{
			// find minimum flow from u to t 
			int curr_flow = min(flow, e.C - e.flow);

			int temp_flow = sendFlow(e.v, curr_flow, t, start);

			// flow is greater than zero 
			if (temp_flow > 0)
			{
				// add flow  to current edge 
				e.flow += temp_flow;

				// subtract flow from reverse edge 
				// of current edge 
				adj[e.v][e.rev].flow -= temp_flow;
				return temp_flow;
			}
		}
	}

	return 0;
}

// Returns maximum flow in graph 
int Graph::DinicMaxflow(int s, int t)
{
	// Corner case 
	if (s == t)
		return -1;

	int total = 0;  // Initialize result 

	// Augment the flow while there is path 
	// from source to sink 
	while (BFS(s, t) == true)
	{
		// store how many edges are visited 
		// from V { 0 to V } 
		int* start = new int[V + 1];

		// while flow is not zero in graph from S to D 
		while (int flow = sendFlow(s, INT_MAX, t, start))

			// Add path flow to overall flow 
			total += flow;
	}

	// return maximum flow 
	return total;
}

// Driver program to test above functions 
int main()
{
	Graph g(6);
	g.addEdge(0, 1, 16);
	g.addEdge(0, 2, 13);
	g.addEdge(1, 2, 10);
	g.addEdge(1, 3, 12);
	g.addEdge(2, 1, 4);
	g.addEdge(2, 4, 14);
	g.addEdge(3, 2, 9);
	g.addEdge(3, 5, 20);
	g.addEdge(4, 3, 7);
	g.addEdge(4, 5, 4);

	// next exmp 
	/*g.addEdge(0, 1, 3 );
	  g.addEdge(0, 2, 7 ) ;
	  g.addEdge(1, 3, 9);
	  g.addEdge(1, 4, 9 );
	  g.addEdge(2, 1, 9 );
	  g.addEdge(2, 4, 9);
	  g.addEdge(2, 5, 4);
	  g.addEdge(3, 5, 3);
	  g.addEdge(4, 5, 7 );
	  g.addEdge(0, 4, 10);

	 // next exp
	 g.addEdge(0, 1, 10);
	 g.addEdge(0, 2, 10);
	 g.addEdge(1, 3, 4 );
	 g.addEdge(1, 4, 8 );
	 g.addEdge(1, 2, 2 );
	 g.addEdge(2, 4, 9 );
	 g.addEdge(3, 5, 10 );
	 g.addEdge(4, 3, 6 );
	 g.addEdge(4, 5, 10 ); */

	cout << "Maximum flow " << g.DinicMaxflow(0, 5);
	return 0;
}

相关文章:

23种设计模式之模板方法模式(模板模式)

分别运行StaffA以及StaffB这两个类我们可以看到整个的步骤顺序是没有变的,只有交通工具以及路线发生了变化,个人认为代码已经诠释了模板模式的精髓,我们平时出现多个类似的功能功能点比如解析DOC文件或者XLSX等不同的文件,前期一些初始化的操作可以封装到模板方法中,到了具体解析哪一种类型的文件,再去交给子类实现。比如,在 Hibernate 中,Session 是一个抽象类,它定义了一系列的模板方法,如 save()、update() 和 delete(),用于执行数据库操作。②:出行交通工具的选择。

23种设计模式之装饰者模式(被装饰者,接口层,装饰抽象层,具体装饰者)

装饰者模式 其核心就是为了增强方法,对原业务的扩展,以上面的场景来说,牧场工作人员只做牛奶的生产,但是想要对牛奶进行销售,只生产是不够的,生产前后都需要增加其他的行为。装饰抽象层存在的意义抽象类实现接口,可以选择性的去实现接口的抽象方法,并不需要重写接口所有方法,装饰抽象层可以精确的让具体装饰者去装饰某一个行为。异变代理模式。

Java 线程的基本概念

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束.程序在 t1 线程运行,FileReader.read() 方法调用是异步的。打断 sleep 的线程, 会清空打断状态,以 sleep 为例。

【Redis】Redis的事务碰上Spring的事务 @Transactional导致问题

建议大家去看原文,这里是学到了,记录一下。最近项目的生产环境遇到一个奇怪的问题:现象 :每天早上客服人员在后台创建客服事件时,都会创建失败。当我们重启 这个微服务后,后台就可以正常创建了客服事件了。到第二天早上又会创建失败,又得重启这个微服务才行。初步排查 :创建一个客服事件时,会用到 Redis 的递增操作来生成一个唯一的分布式 ID 作为事件 id。而恰巧每天早上这个递增操作都会返回 null,进而导致后面的一系列逻辑出错,保存客服事件失败。当重启微服务后,这个递增操作又正常了。

【redis】bitmap技术解析:redis与roaringBitmap

然后再看这个好理解一些。bitmap的表象意义是,使用一个01标识位来表示是否的状态,可以达到节省空间和高效判定的效果。在我们的实际工作中,也有着许多的应用场景,相信了解bitmap定会给你带来一些额外的收获。

【Linux系统化学习】进程的父子关系 | fork 进程

本篇文章介绍了进程的父子关系和使用fork函数创建一个进程!

【Linux系统化学习】探索进程的奥秘 | 第一个系统调用

本片文章主要介绍了Linux下的进程和第一个系统调用。

【C++干货铺】解密vector底层逻辑

本片文章主要是vector的介绍使用和手撕模拟实现!!!!

【C++干货铺】剖析string | 底层实现

探索string底层,模拟实现string。

【Linux系统化学习】冯诺依曼体系结构 | 操作系统

本篇文章介绍了冯诺依曼体系结构,描述了操作系统。

【C++干货铺】STL简述 | string类的使用指南

STL简述!!! string类接口使用指南!!!

【C++干货铺】初识模板

初识C++模板!!!函数模板&类模板!!!超详解!!!

【C++干货铺】内存管理new和delete

C&C++内存管理!!!malloc/realloc/calloc/free!!!new/delete!!!

人工智能时代:AIGC的横空出世

AIGC是一种新的人工智能技术,即人工智能生成内容。它是一种基于机器学习和自然语言处理的技术,能够自动产生文本、图像、音频等多种类型的内容。

零基础搭建本地Nextcloud私有云结合内网穿透实现远程访问

本文主要讲解如何搭建本地Nextcloud私有云结合内网穿透实现远程访问

如何在外远程访问本地NAS威联通QNAP?

本文主要讲解在外远程访问本地NAS威联通QNAP。

使用Linux JumpServer堡垒机本地部署与远程访问

本文主要讲解如何使用Linux JumpServer堡垒机本地部署与远程访问。

Java中判断两个Long类型是否相等

也就是说这个值在-128到127之间会使用缓存,超过就会创建一个对象,所以上述的两个值分别创建了两个对象,那么使用。可以使用 .longValue() 或 .equals() 进行比较。推荐使用equals方法进行比较。现象1和现象2结果不一样,现象2使用==判断两个Long类型的值,结果竟然是false!

Nginx反向代理跳过国内备案(以宝塔面板为例)

Nginx代理跳过大陆备案验证,需要两台服务器,一台已备案或者免备案,一台国内主力服务器放你的项目。B服务器在配置文件里设置listen监听端口号。先把域名解析到A服务器。然后在A服务器里配置。

大华摄像头windows、linuxJavaSDK开发使用

本文档主要介绍 SDK 接口参考信息,包括主要功能、接口函数和回调函数。主要功能包括:SDK 初始化、设备登录、实时预览、云台控制、语音对讲、报警监听、智能订阅、录像回放和录像下载等。根据环境不同,开发包包含的文件会不同,具体如下所示。Windows 开发包所包含的文件如下:Linux 开发包所包含的文件如下:SDK 的功能库和配置库是必备库。功能库是设备网络 SDK 的主体,主要用于网络客户端与各类产品之间的通讯交互,负责远程控制、查询、配置及码流数据的获取和处理等。

什么是缓存穿透、缓存击穿、缓存雪崩,以及各自的解决方案

当缓存数据大面积失效,导致请求无法从缓存中拿到数据而是直接访问数据库。

并发编程的基本概念

单核 cpu 下,线程实际还是 串行执行 的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows下时间片最小约为 15 毫秒)分给不同的程序使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是 同时运行的。总结为一句话就是:微观串行,宏观并行,多核 cpu下,每个 核(core) 都可以调度运行线程,这时候线程可以是并行的。一般会将这种 线程轮流使用 CPU 的做法称为并发, concurrent。

try catch 应该在 for 循环里面还是外面?

有个老哥昨天被面试官欺负了,但是是被这个问题(标题)欺负的?其实是个比较基础的问题,只要有了解过,叙述是非常简单OK的。

储存容量单位:Bit, Byte, KB, MB, GB, TB , PB, EB, ZB, YB等的关系

有趣的是从 Wikipedia 看到的单位英文在「十进位」与「二进位」不同进制之间所使用的英文单字是不太一样的,例如我们常讲 30GB 会唸成 30 Gigabytes,不过正确的唸法应该是 Gibibytes 才对,不过大家都随便念、随便写,反正差不多、听的懂就好,我想唸过计算机概论的人自己都会知道 1GB = 1024 MB 吧,如果唸成 Gibibytes 搞不好还会被笑没知识!这样大的数据单位估计在未来的五年内是无法达到的,不过我相信假以时日人类的需求一定能够达到或者超越CB级。

Jmeter执行接口自动化测试-如何初始化清空旧数据

生命不息,奋斗不止。每一份努力都不会被辜负,只要坚持不懈,终究会有回报。珍惜时间,追求梦想。不忘初心,砥砺前行。你的未来,由你掌握!生命短暂,时间宝贵,我们无法预知未来会发生什么,但我们可以掌握当下。珍惜每一天,努力奋斗,让自己变得更加强大和优秀。坚定信念,执着追求,成功终将属于你!只有不断地挑战自己,才能不断地超越自己。坚持追求梦想,勇敢前行,你就会发现奋斗的过程是如此美好而值得。相信自己,你一定可以做到!

带有 RaspiCam 的 Raspberry Pi 监控和延时摄影摄像机

​一段时间以来,我一直想构建一个运动激活且具有延时功能的树莓派相机,但从未真正找到我喜欢的案例。我在thingiverse上找到了这个适合树莓派和相机的好案例。它是为特定的鱼眼相机设计的,但从模型来看,我拥有的廉价中国鱼眼手机镜头之一似乎非常适合孔中。​

Nginx基础篇:Nginx搭建、Nginx反向代理、文件服务器部署配置。

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,公开版本1.19.6发布于2020年12月15日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2022年01月25日,nginx 1.21.6发布。

如何在Android平板上远程连接Ubuntu服务器使用code-server代码开发

如何在Android平板上远程连接Ubuntu服务器使用code-server代码开发

使用Pytorch实现Grad-CAM并绘制热力图

就直接从官方的torch vision这个库当中导入一些我们常用的model。比如说我这里的例子是采用的mobile net v3 large这个模型。它就会自动的去下载torch官方在imagenet上预训练好的模型权重。我们这里采用的模型呢是在image net 1k上预训练好的模型。接着这里的target layers也要根据你自己的模型去设置。首先呢我们这里需要指定一下我们的target layers。紧接着呢我们还需要去指定一下我们所感兴趣的这个类别。

CentOS最小化安装后怎么转图形界面/可视化桌面?

2.如果在图形界面下,按:Ctrl+Alt+F2/F3,可以进入命令行模式。如果安装的是最小化,那么init 5 (进入图像化桌面)命令是无效的。1.如果在命令行模式,按Ctrl+Alt+F1,可以进入图形界面;设置完后,用上一条命令:systemctl get-default。注意:进入图形界面要创建一个新用户,同样要记好账号和密码。查看是否返回:Graphical.target。图形界面:终端输入init 3进入命令行。命令行:输入init 5进入图形。设置默认启动桌面(看个人喜欢)