full file
This commit is contained in:
parent
9455158469
commit
3c3cd19ec6
61
MaxFlow.py
Normal file
61
MaxFlow.py
Normal file
@ -0,0 +1,61 @@
|
||||
from collections import deque
|
||||
|
||||
|
||||
class MaxFlow:
|
||||
def __init__(self, vertices):
|
||||
self.V = vertices # 图的顶点数
|
||||
self.graph = [[] for _ in range(vertices)] # 邻接表表示图
|
||||
self.level = [-1] * vertices # 层次标记
|
||||
self.ptr = [0] * vertices # 每个节点在其邻接边中的指针
|
||||
|
||||
def add_edge(self, u, v, capacity):
|
||||
"""添加从u到v的边,并反向边的容量初始化为0"""
|
||||
self.graph[u].append([v, capacity, len(self.graph[v])]) # (目标点, 容量, 反向边的索引)
|
||||
self.graph[v].append([u, 0, len(self.graph[u]) - 1]) # (源点, 容量0, 反向边的索引)
|
||||
|
||||
def bfs(self, source, sink):
|
||||
"""BFS进行分层图构建"""
|
||||
queue = deque([source])
|
||||
self.level = [-1] * self.V
|
||||
self.level[source] = 0
|
||||
|
||||
while queue:
|
||||
u = queue.popleft()
|
||||
|
||||
for v, cap, _ in self.graph[u]:
|
||||
if self.level[v] == -1 and cap > 0: # 未访问且容量大于0
|
||||
self.level[v] = self.level[u] + 1
|
||||
if v == sink:
|
||||
return True
|
||||
queue.append(v)
|
||||
|
||||
return False
|
||||
|
||||
def dfs(self, u, sink, flow):
|
||||
"""DFS寻找增广路径并更新残余图"""
|
||||
if u == sink:
|
||||
return flow
|
||||
while self.ptr[u] < len(self.graph[u]):
|
||||
v, cap, rev = self.graph[u][self.ptr[u]]
|
||||
if self.level[v] == self.level[u] + 1 and cap > 0:
|
||||
current_flow = min(flow, cap)
|
||||
result = self.dfs(v, sink, current_flow)
|
||||
if result > 0:
|
||||
# 更新正向边和反向边
|
||||
self.graph[u][self.ptr[u]][1] -= result
|
||||
self.graph[v][rev][1] += result
|
||||
return result
|
||||
self.ptr[u] += 1
|
||||
return 0
|
||||
|
||||
def dinic(self, source, sink):
|
||||
"""Dinic算法计算最大流"""
|
||||
max_flow = 0
|
||||
while self.bfs(source, sink):
|
||||
self.ptr = [0] * self.V
|
||||
while True:
|
||||
flow = self.dfs(source, sink, float('Inf'))
|
||||
if flow == 0:
|
||||
break
|
||||
max_flow += flow
|
||||
return max_flow
|
116
gpt/code.md
Normal file
116
gpt/code.md
Normal file
@ -0,0 +1,116 @@
|
||||
好的,以下是使用 **Dinic's Algorithm** 实现最大流问题的 Python 代码。Dinic 算法比 Edmonds-Karp 更高效,尤其是在稀疏图中。它使用分层网络和每一层的增广路径来提高效率。
|
||||
|
||||
### **Dinic算法的Python实现:**
|
||||
|
||||
```python
|
||||
from collections import deque
|
||||
|
||||
class MaxFlow:
|
||||
def __init__(self, vertices):
|
||||
self.V = vertices # 图的顶点数
|
||||
self.graph = [[] for _ in range(vertices)] # 邻接表表示图
|
||||
self.level = [-1] * vertices # 层次标记
|
||||
self.ptr = [0] * vertices # 每个节点在其邻接边中的指针
|
||||
|
||||
def add_edge(self, u, v, capacity):
|
||||
"""添加从u到v的边,并反向边的容量初始化为0"""
|
||||
self.graph[u].append([v, capacity, len(self.graph[v])]) # (目标点, 容量, 反向边的索引)
|
||||
self.graph[v].append([u, 0, len(self.graph[u]) - 1]) # (源点, 容量0, 反向边的索引)
|
||||
|
||||
def bfs(self, source, sink):
|
||||
"""BFS进行分层图构建"""
|
||||
queue = deque([source])
|
||||
self.level = [-1] * self.V
|
||||
self.level[source] = 0
|
||||
|
||||
while queue:
|
||||
u = queue.popleft()
|
||||
|
||||
for v, cap, _ in self.graph[u]:
|
||||
if self.level[v] == -1 and cap > 0: # 未访问且容量大于0
|
||||
self.level[v] = self.level[u] + 1
|
||||
if v == sink:
|
||||
return True
|
||||
queue.append(v)
|
||||
|
||||
return False
|
||||
|
||||
def dfs(self, u, sink, flow):
|
||||
"""DFS寻找增广路径并更新残余图"""
|
||||
if u == sink:
|
||||
return flow
|
||||
while self.ptr[u] < len(self.graph[u]):
|
||||
v, cap, rev = self.graph[u][self.ptr[u]]
|
||||
if self.level[v] == self.level[u] + 1 and cap > 0:
|
||||
current_flow = min(flow, cap)
|
||||
result = self.dfs(v, sink, current_flow)
|
||||
if result > 0:
|
||||
# 更新正向边和反向边
|
||||
self.graph[u][self.ptr[u]][1] -= result
|
||||
self.graph[v][rev][1] += result
|
||||
return result
|
||||
self.ptr[u] += 1
|
||||
return 0
|
||||
|
||||
def dinic(self, source, sink):
|
||||
"""Dinic算法计算最大流"""
|
||||
max_flow = 0
|
||||
while self.bfs(source, sink):
|
||||
self.ptr = [0] * self.V
|
||||
while True:
|
||||
flow = self.dfs(source, sink, float('Inf'))
|
||||
if flow == 0:
|
||||
break
|
||||
max_flow += flow
|
||||
return max_flow
|
||||
|
||||
|
||||
# 示例
|
||||
if __name__ == "__main__":
|
||||
vertices = 6
|
||||
g = MaxFlow(vertices)
|
||||
|
||||
# 添加边及其容量
|
||||
g.add_edge(0, 1, 16)
|
||||
g.add_edge(0, 2, 13)
|
||||
g.add_edge(1, 2, 10)
|
||||
g.add_edge(1, 3, 12)
|
||||
g.add_edge(2, 1, 4)
|
||||
g.add_edge(2, 4, 14)
|
||||
g.add_edge(3, 2, 9)
|
||||
g.add_edge(3, 5, 20)
|
||||
g.add_edge(4, 3, 7)
|
||||
g.add_edge(4, 5, 4)
|
||||
|
||||
source = 0 # 源点
|
||||
sink = 5 # 汇点
|
||||
|
||||
max_flow = g.dinic(source, sink)
|
||||
print("最大流为:", max_flow)
|
||||
```
|
||||
|
||||
### **代码解释:**
|
||||
1. **添加边:** `add_edge(u, v, capacity)` 方法将一条从节点 `u` 到 `v` 的边加入图中,且边的容量为 `capacity`。同时,在反向边上初始化容量为 0。
|
||||
2. **BFS(层次图构建):** `bfs(source, sink)` 方法通过广度优先搜索构建分层网络,标记每个节点的层次,帮助找增广路径。
|
||||
3. **DFS(增广路径):** `dfs(u, sink, flow)` 方法通过深度优先搜索,在层次网络中寻找增广路径并进行流量传递。
|
||||
4. **Dinic算法:** `dinic(source, sink)` 方法利用层次图和增广路径相结合的方式进行多轮流量增广,直到没有更多增广路径为止。
|
||||
|
||||
### **算法分析:**
|
||||
1. **时间复杂度:**
|
||||
- 每轮BFS的时间复杂度为 \(O(V + E)\),
|
||||
- 每轮DFS的时间复杂度为 \(O(E)\)。
|
||||
- 总体时间复杂度为 \(O(V^2 E)\),在稀疏图中,相较于Edmonds-Karp更为高效。
|
||||
|
||||
2. **空间复杂度:**
|
||||
- 使用了邻接表来表示图,空间复杂度为 \(O(V + E)\),其中V为节点数,E为边数。
|
||||
|
||||
### **实验输出:**
|
||||
运行代码后,输出的最大流值如下:
|
||||
```
|
||||
最大流为: 23
|
||||
```
|
||||
|
||||
### **总结:**
|
||||
使用 Dinic 算法可以在稀疏图中高效计算最大流,比 Edmonds-Karp 更加高效,尤其在实际问题中,当图的边数和顶点数较大时,Dinic 算法的表现尤为突出。
|
||||
|
||||
如果有任何问题或者需要进一步的解释,随时告诉我!
|
63
gpt/complex.md
Normal file
63
gpt/complex.md
Normal file
@ -0,0 +1,63 @@
|
||||
我们来分析 **Dinic算法** 的时间复杂度和空间复杂度。
|
||||
|
||||
### **1. 时间复杂度分析**
|
||||
|
||||
**Dinic算法**的时间复杂度主要由两部分组成:**层次图构建**(BFS)和**增广路径查找**(DFS)。
|
||||
|
||||
#### (1) **BFS(层次图构建)**
|
||||
每次运行BFS时,我们会遍历所有的边并更新层次信息,因此BFS的时间复杂度是 \(O(V + E)\),其中:
|
||||
- \(V\) 是图中的顶点数,
|
||||
- \(E\) 是图中的边数。
|
||||
|
||||
#### (2) **DFS(增广路径查找)**
|
||||
DFS的作用是通过递归寻找增广路径,并更新残留图中的流量。在每一次的增广过程中,DFS会沿着层次图进行搜索,每个边最多只会被访问一次。因此,每次DFS的复杂度也是 \(O(E)\)。
|
||||
|
||||
#### (3) **迭代次数**
|
||||
- 在Dinic算法中,每一轮都先执行一次BFS来构建分层图,并在该分层图上执行多次DFS增广路径查找。由于每次BFS构建的层次图是严格的,每次DFS都只能沿着一个分层图进行路径查找。
|
||||
- 每轮BFS都会增加一个新的层次,直到没有更多的增广路径为止。
|
||||
- 在每一轮增广过程中,每条边最多只能传递一次流量,这意味着每条边最多会被多次DFS访问。由于DFS会按层次增广流量,且每次增广的流量是非零的,因此每条边的增广次数是有限的。
|
||||
|
||||
**最大流的计算复杂度:**
|
||||
假设图中有 \(V\) 个顶点,\(E\) 条边。
|
||||
- 每次BFS的复杂度为 \(O(V + E)\)。
|
||||
- 在每轮BFS之后,最多会有 \(O(V)\) 次DFS,每次DFS的复杂度为 \(O(E)\)。
|
||||
- 因此,Dinic算法的时间复杂度是:
|
||||
\[
|
||||
O(V^2 E)
|
||||
\]
|
||||
这是由于在最坏情况下,增广路径的数量可能达到 \(O(V)\),并且每次增广路径都会涉及 \(O(E)\) 的复杂度。
|
||||
|
||||
**简化版:**
|
||||
在稀疏图中,边的数量 \(E\) 远小于 \(V^2\),因此有时我们可以近似认为复杂度为 \(O(V E)\)。
|
||||
|
||||
#### **结论:**
|
||||
Dinic算法的时间复杂度为 \(O(V^2 E)\),其中 \(V\) 是顶点数,\(E\) 是边数。
|
||||
|
||||
### **2. 空间复杂度分析**
|
||||
|
||||
Dinic算法的空间复杂度主要由以下几部分组成:
|
||||
|
||||
#### (1) **图的表示**
|
||||
- 我们使用邻接表来表示图,每个节点的邻接列表包含所有从该节点出发的边。
|
||||
- 在最坏的情况下,邻接表中每个节点会有最多 \(E\) 条边,因此图的存储空间为 \(O(V + E)\)。
|
||||
|
||||
#### (2) **层次图(Level)和指针数组(Ptr)**
|
||||
- `level` 数组用于存储每个节点的层次信息,大小为 \(O(V)\)。
|
||||
- `ptr` 数组用于存储每个节点的指针,指向当前DFS遍历时的边,大小也为 \(O(V)\)。
|
||||
|
||||
#### (3) **其他辅助变量**
|
||||
- 如队列(用于BFS)、递归栈(用于DFS)等,空间复杂度也为 \(O(V)\)(最坏情况下,队列和递归栈的深度可能都与图的顶点数成比例)。
|
||||
|
||||
因此,**总的空间复杂度**为:
|
||||
\[
|
||||
O(V + E)
|
||||
\]
|
||||
这是由于图的邻接表占用了 \(O(V + E)\) 的空间,层次图和指针数组占用了 \(O(V)\) 的空间。
|
||||
|
||||
### **总结:**
|
||||
- **时间复杂度:** \(O(V^2 E)\)
|
||||
- **空间复杂度:** \(O(V + E)\)
|
||||
|
||||
Dinic算法在稀疏图中的表现更好,比 Edmonds-Karp 算法具有更优的时间复杂度。
|
||||
|
||||
如果你有其他问题或需要进一步详细分析,请告诉我!
|
39
gpt/conception.md
Normal file
39
gpt/conception.md
Normal file
@ -0,0 +1,39 @@
|
||||
### **Dinic算法的基本思想**
|
||||
|
||||
Dinic算法是一种求解**最大流问题**的高效算法,特别适用于稀疏图。它通过**分层图**和**分段增广**的策略来加速最大流的计算,能够在一定程度上避免重复的增广操作,因此相比于传统的**Ford-Fulkerson算法**或**Edmonds-Karp算法**(基于广度优先搜索),它通常更为高效。
|
||||
|
||||
**Dinic算法**的基本思想可以分为以下几个步骤:
|
||||
|
||||
### 1. **分层图(Level Graph)构建**
|
||||
在Dinic算法中,首先通过广度优先搜索(BFS)构建一个**分层图**,该图将源点到汇点的路径分配不同的层次信息。每个节点会被分配一个层次值,表示从源点到该节点的最短路径中边的数量。这个过程的目的是:
|
||||
- **层次图的作用:** 层次图确保增广路径不会反复走回到已经增广过的路径,避免了冗余计算,提高了算法的效率。
|
||||
- **BFS的作用:** BFS遍历图,给每个节点分配层次,直到没有可达的节点或汇点被层次标记。
|
||||
|
||||
### 2. **增广路径查找**
|
||||
在分层图构建完成后,算法进入增广阶段:
|
||||
- **DFS增广:** 对于每个层次图中的路径,使用深度优先搜索(DFS)找出增广路径,并沿着路径增加流量。
|
||||
- 每次DFS会根据分层图的层次关系,从源点开始,沿着层次较低的节点流动,直到到达汇点。
|
||||
- 每次DFS找到的增广路径,都会增加一定的流量,同时更新残余图中的边容量。
|
||||
- **增广路径的特点:** 由于分层图的限制,DFS只能沿着层次递增的边进行增广,这保证了每次增广路径的流量传递不会重复走回头路。
|
||||
|
||||
### 3. **多轮增广(分段增广)**
|
||||
- Dinic算法的核心在于分段增广。即在每一轮构建层次图并找到增广路径后,更新残余图,再次开始新的增广过程,直到无法找到增广路径为止。
|
||||
- 每次增广操作(BFS + 多次DFS)会处理一段流量,直到无法再找到增广路径为止。这样避免了重复的增广路径查找。
|
||||
|
||||
### 4. **停止条件**
|
||||
当层次图构建完成后,若源点到汇点之间不存在可增广的路径时,算法结束,此时的流量即为最大流量。
|
||||
|
||||
### **算法的核心优势**
|
||||
- **分层图:** 每次增广操作通过层次图来限定增广路径的选择,减少了冗余的增广,避免了无效的反向增广路径。
|
||||
- **分段增广:** 在每一轮中,分层图的增广使得流量更加集中地通过较短的路径传递,提高了每轮增广的效率。
|
||||
- **改进的BFS和DFS:** 利用BFS和DFS相结合的策略,Dinic算法优化了Ford-Fulkerson和Edmonds-Karp中路径搜索的效率,特别是在稀疏图中,性能显著优于后者。
|
||||
|
||||
### **总结**
|
||||
Dinic算法的基本思想可以概括为:
|
||||
- **BFS**用于构建分层图,标记从源点到汇点的最短路径层次;
|
||||
- **DFS**在分层图上寻找增广路径并更新流量;
|
||||
- **分段增广**通过每轮增广操作,逐渐逼近最大流,直到没有增广路径为止。
|
||||
|
||||
Dinic算法的优势在于其较高的效率,特别是在稀疏图上,相比Edmonds-Karp算法,它能够减少不必要的增广路径查找,达到更优的性能。
|
||||
|
||||
如果需要进一步详细理解某个步骤或其他问题,随时告诉我!
|
77
gpt/explain.md
Normal file
77
gpt/explain.md
Normal file
@ -0,0 +1,77 @@
|
||||
在 **Dinic算法** 的 Python 实现中,我们使用了多个函数来实现最大流的计算。以下是每个函数的详细解析:
|
||||
|
||||
### **1. `__init__(self, vertices)`**
|
||||
- **功能:** 初始化图,设置节点数量,初始化图的存储结构。
|
||||
- **参数:**
|
||||
- `vertices`: 图中顶点的数量 \(V\)。
|
||||
- **实现:**
|
||||
`self.graph` 是一个包含邻接列表的数组,每个节点的邻接列表保存了与该节点相连的边的信息。`self.level` 用于存储每个节点的层次信息,`self.ptr` 用于在 DFS 中标记每个节点的邻接边的遍历位置。
|
||||
|
||||
### **2. `add_edge(self, u, v, capacity)`**
|
||||
- **功能:** 向图中添加一条边,并记录反向边的容量为 0。
|
||||
- **参数:**
|
||||
- `u`: 边的起点。
|
||||
- `v`: 边的终点。
|
||||
- `capacity`: 边的容量。
|
||||
- **实现:**
|
||||
1. 该函数将一条从 `u` 到 `v` 的边加入邻接表,并将边的容量设置为 `capacity`。
|
||||
2. 同时,加入一条反向边,即从 `v` 到 `u` 的边,容量初始化为 0。
|
||||
3. 每个边除了目标节点外,还记录反向边的索引,以便在增广流量时更新反向边的容量。
|
||||
|
||||
### **3. `bfs(self, source, sink)`**
|
||||
- **功能:** 使用广度优先搜索(BFS)来构建层次图,并检查从源点 `source` 到汇点 `sink` 是否存在增广路径。
|
||||
- **参数:**
|
||||
- `source`: 源点。
|
||||
- `sink`: 汇点。
|
||||
- **返回:**
|
||||
- 如果从源点到汇点有增广路径,返回 `True`;否则返回 `False`。
|
||||
- **实现:**
|
||||
- BFS 在图中按层次进行搜索,层次较浅的节点先被访问。每次找到一个节点的邻接边可行且未被访问,则将该节点加入队列,并标记它的层次。
|
||||
- BFS 在搜索过程中会更新每个节点的层次信息,层次值是从源点到该节点的最短路径中包含的边的数量。
|
||||
- 一旦找到汇点,说明从源点到汇点存在增广路径,返回 `True`,否则返回 `False`。
|
||||
|
||||
### **4. `dfs(self, u, sink, flow)`**
|
||||
- **功能:** 使用深度优先搜索(DFS)沿着分层图寻找增广路径,并沿路径更新流量。
|
||||
- **参数:**
|
||||
- `u`: 当前节点。
|
||||
- `sink`: 汇点。
|
||||
- `flow`: 当前可以通过路径传输的最大流量。
|
||||
- **返回:**
|
||||
- 如果找到增广路径,返回增广路径的流量。
|
||||
- 如果没有找到增广路径,返回 0。
|
||||
- **实现:**
|
||||
- DFS 在分层图中搜索增广路径,保证增广路径的流量是沿着层次递增的方向传递的。
|
||||
- 每次递归时,通过遍历节点 `u` 的邻接边,检查每条边的容量。如果该边的容量大于 0 且目标节点的层次比当前节点大 1,则继续递归进行流量传递。
|
||||
- 如果找到增广路径,更新路径上的边的流量,并返回该路径的流量。
|
||||
- 如果 DFS 没有找到增广路径,返回 0。
|
||||
|
||||
### **5. `dinic(self, source, sink)`**
|
||||
- **功能:** 执行 Dinic 算法,通过多轮分层图和增广路径更新流量,计算从源点 `source` 到汇点 `sink` 的最大流量。
|
||||
- **参数:**
|
||||
- `source`: 源点。
|
||||
- `sink`: 汇点。
|
||||
- **返回:**
|
||||
- 返回最大流量。
|
||||
- **实现:**
|
||||
1. 初始化 `max_flow` 为 0,表示当前已找到的最大流量。
|
||||
2. 进入一个循环,直到找不到增广路径为止:
|
||||
- 使用 BFS 构建层次图。
|
||||
- 如果 BFS 返回 `True`(存在增广路径),则使用 DFS 进行增广,更新流量。
|
||||
- 每次找到增广路径后,更新 `max_flow`。
|
||||
3. 当 BFS 无法找到增广路径时,算法终止,返回 `max_flow` 作为最大流。
|
||||
|
||||
---
|
||||
|
||||
### **总结**
|
||||
- `__init__`: 初始化图的结构。
|
||||
- `add_edge`: 添加边并记录反向边。
|
||||
- `bfs`: 构建层次图,寻找增广路径。
|
||||
- `dfs`: 在分层图中执行深度优先搜索,更新流量。
|
||||
- `dinic`: 执行 Dinic 算法的主函数,通过多轮增广计算最大流。
|
||||
|
||||
### **算法工作流程**
|
||||
1. **构建层次图(BFS)**:用 BFS 来构建源点到汇点的分层图,将节点分层,保证增广路径的流量传递顺序。
|
||||
2. **查找增广路径(DFS)**:使用 DFS 在分层图中找增广路径,并沿路径传递流量。
|
||||
3. **多轮增广**:每次更新流量,直到无法找到增广路径为止。
|
||||
|
||||
Dinic算法通过分层图的优化和分段增广策略,减少了冗余的路径搜索,提高了算法效率。
|
24
main.py
Normal file
24
main.py
Normal file
@ -0,0 +1,24 @@
|
||||
# 示例
|
||||
from MaxFlow import MaxFlow
|
||||
|
||||
if __name__ == "__main__":
|
||||
vertices = 6
|
||||
g = MaxFlow(vertices)
|
||||
|
||||
# 添加边及其容量
|
||||
g.add_edge(0, 1, 16)
|
||||
g.add_edge(0, 2, 13)
|
||||
g.add_edge(1, 2, 10)
|
||||
g.add_edge(1, 3, 12)
|
||||
g.add_edge(2, 1, 4)
|
||||
g.add_edge(2, 4, 14)
|
||||
g.add_edge(3, 2, 9)
|
||||
g.add_edge(3, 5, 20)
|
||||
g.add_edge(4, 3, 7)
|
||||
g.add_edge(4, 5, 4)
|
||||
|
||||
source = 0 # 源点
|
||||
sink = 5 # 汇点
|
||||
|
||||
max_flow = g.dinic(source, sink)
|
||||
print("最大流为:", max_flow)
|
Loading…
Reference in New Issue
Block a user