Luồng Cực đại - Tuoitrekthc
Có thể bạn quan tâm
1. Mạng
Một đa đồ thị có hướng, không khuyên, có một đỉnh phát, một đỉnh thu và có hàm c ánh xạ từ tập cung vào tập số thực không âm tạo thành một mạng. Ký hiệu mạng G là bộ năm (V, E, c, s, t) trong đó V là tập đỉnh, E là tập cung, s là đỉnh phát, t là đỉnh thu, c: E→[0;+∞) cho tương ứng mỗi cung e với số không âm c(e) gọi là sức chứa trên cung e.
2. Luồng
Luồng trên mạng G(V, E, c, s, t) là một hàm f ánh xạ từ tập cung E vào tập số thực: f: E→(-∞; +∞) mà thỏa mãn 3 ràng buộc sau đây:
- Trên mọi cung e: f(e) ≤ c(e), ở đây f(e) là luồng trên cung e.
- Trên mọi cung e: f(e) = – f(-e) trong đó qui ước cung -e là cung ngược chiều của cung e.
- Tại mọi đỉnh v khác s và t, tổng luồng trên các cung đi ra khỏi v bằng 0.
Từ 3 ràng buộc trên có thể suy ra: Tại mọi đỉnh v khác s và t, tổng luồng trên các cung đi vào v cũng bằng 0.
Giá trị của một luồng là tổng luồng trên các cung đi ra khỏi đỉnh phát s.
3. Luồng dương
Luồng dương trên mạng G(V, E, c, s, t) là một hàm φ ánh xạ từ tập cung E vào tập số thực: φ: E→[0; +∞) mà thỏa mãn 2 ràng buộc sau đây:
- Trên mọi cung e: 0≤φ(e) ≤ c(e)
- Tại mọi đỉnh v khác s và t, tổng luồng trên các cung đi ra khỏi v=tổng luồng trên các cung đi vào v.
Giá trị của luồng dương bằng tổng luồng này trên các cung đi ra khỏi đỉnh phát trừ đi tổng luồng này trên các cung đi vào đỉnh phát.
Hệ quả. Giá trị của luồng dương bằng tổng luồng này trên các cung đi vào đỉnh thu trừ đi tổng luồng này trên các cung ra khỏi đỉnh thu.
Các cung e nếu có f(e)=c(e) thì gọi e là cung bão hòa, cung e có f(e)<c(e) gọi là cung thặng dư (có thể cho luồng tăng trên cung này một lượng tối đa là cf(e)=c(e)-f(e))
4. Đường tăng luồng
Đường tăng luồng là đường đi đơn từ đỉnh s đến đỉnh t, chỉ gồm các cung thặng dư. Giá trị tăng luồng trên đường tăng luồng là giá trị nhỏ nhất của c(e)-f(e) trên các cung e thuộc đường tăng luồng (chung cho cả cung thuận và cung ngược trên đường tăng luồng).
5. Bài toán luồng cực đại
Bài toán luồng cực đại là:
Cho một mạng G, hãy tìm luồng có giá trị lớn nhất trên mạng.
a) Thuật toán Ford-Fulkerson:
- Xuất phát từ một luồng f tùy ý trên mạng (thường chọn là f(e)=0 với mọi cung e).
- Lặp {
- Tìm đường tăng luồng (và tìm có giá trị tăng luồng tại các đỉnh v là ∆(v)=min{∆(u), C[u,v]-f[u][v]}) với mọi cung (u;v) trên đường tăng luồng.
- Tăng luồng f = f+∆(t) trên các cung của đường tăng luồng, đồng thời giảm giá trị luồng trên các cung đối một lượng ∆(t).
} cho đến khi không tìm được đường tăng luồng.
Thuật toán Ford-Fulkerson với kỹ thuật tìm đường tăng luồng bằng BFS gọi là thuật toán Edmonds-Karp có thời gian thực hiện là O(|V|.|E|2):
Chương trình 1. Dùng ma trận trọng số trên đồ thị thặng dư (đồ thị gồm các cung thặng dư, luôn biến động vì các cung thặng dư sau mỗi lần tăng luồng lại cập nhật giá trị mới)
#include <iostream>
#include <fstream>
#include <queue>
#define maxN 100
using namespace std;
ifstream fi (“MAXFLOW1.INP”);
ofstream fo (“MAXFLOW1.OUT”);
int n, m, s, t;
int G[maxN][maxN]; // Đồ thị ban đầu
int rG[maxN][maxN]; // Đồ thị gồm các cung thặng dư
int parent[maxN];
bool bfs(){ // Tìm đường tăng luồng bằng BFS
bool visited[maxN];
memset(visited, 0, sizeof(visited)); // Đánh dấu các đỉnh chưa thăm
queue <int> q; // Hàng đợi để tìm các đỉnh của đường tăng luồng
q.push(s); // Nạp đỉnh phát s vào hàng đợi
visited[s] = true; // Đã thăm s
parent[s] = -1; // Đỉnh trước đỉnh s coi như -1
while (!q.empty()) { // Trong khi hàng đợi chưa đầy, còn tìm kiếm BFS
int u = q.front();// Lấy một đỉnh ở đầu hàng đợi
q.pop();// Xóa đỉnh ở đầu hàng đợi
for (int v=0; v<n; v++){ // Xét các đỉnh kề v : có cung (u;v)
if (visited[v]==false && rG[u][v] > 0){ //v chưa thăm, (u;v) còn sức chứa
q.push(v); // thì nạp v
parent[v] = u; // Lưu vết trước v là u
visited[v] = true; // Đánh dấu đã thăm v
}
}
}
return (visited[t] == true); // thăm được tới t thì có đường tăng luồng
}
int fordFulkerson(){// Thuật toán FordFulkenson
int u, v;
int max_flow = 0; // Khởi trị giá trị max luồng
while (bfs()) { // Trong khi còn tìm được đường tăng luồng thì
int delta = INT_MAX; // Khởi trị delta (giá trị tăng luồng trên một đường tăng luồng)
for (v=t; v!=s; v=u){ // Lần ngược vết đường tăng luồng
u = parent[v];
delta = min(delta, rG[u][v]); // Cập nhật giá trị delta
}
for (v=t; v != s; v=u){ // Cập nhật các cung thặng dư và cung ngược
u = parent[v];
rG[u][v] -= delta;
rG[v][u] += delta;
}
max_flow += delta; // Cập nhật max luồng
}
return max_flow; // Trả về max luồng
}
void read_input(){
int i, j, w;
fi>> n >> m >> s >> t ;
for (int e=1; e<=m; e++){
fi >> i >> j >> w;
G[i][j] = w;
}
memmove(rG, G, sizeof(G));
}
int main(){
read_input();
fo << fordFulkerson() << endl;
for (int u=0; u<n; u++)
for (int v=0; v<n; v++)
if (G[u][v]-rG[u][v]>0) // Có luồng trên cung (u;v)
fo << u << ” ” << v << ” ” << G[u][v]-rG[u][v] << endl;
return 0;
}
Chương trình 2. Thực hiện thuật toán Edmonds-Karp với cài đặt dữ liệu dựa vào ma trận trọng số, danh sách kề của đồ thị c ban đầu, xây dựng đồ thị luồng f và cập nhật sau mỗi lần tăng luồng:
#include <iostream>
#include <fstream>
#include<queue>
#include<vector>
#define maxN 1000
#define INF 2147483646
using namespace std;
ifstream fi (“MAXFLOW.INP”);
ofstream fo (“MAXFLOW.OUT”);
int c[maxN][maxN]; // Sức chứa trên các cung dưới dạng ma trận trọng số
int f[maxN][maxN]; // Luồng trên các cung
int trace[maxN]; // Lưu vết các đỉnh trên một đường tăng luồng
vector<int> adj[maxN];// toàn bộ danh sách kề của mỗi đỉnh (mỗi danh sách là một vector)
int n, m, s, t; // Số dỉnh, số cung, đỉnh phát, đỉnh thu
int bfs(){// Tìm kiếm theo chiều rộng BFS để tìm các đường tăng luồng
int delta[maxN]; // Lượng tối đa còn cho phép luồng thông qua mỗi đỉnh
queue<int> q; // Hàng đợi dùng thực hiện BFS
memset(trace, -1, sizeof(trace)); // Xin vùng nhớ và khởi trị mảng trace, mảng delta
memset(delta, 0, sizeof(delta));
q.push(s); // Nạp đỉnh phát s vào hàng đợi
trace[s]=-2; // Đánh dấu đặc biệt đối với đỉnh phát đã thăm (bằng -2)
delta[s]=INF; // Khởi trị khả năng luồng tối đa qua đỉnh phát là vô cùng
while(!q.empty()) { // Trong khi hàng đợi chưa rỗng còn tìm kiếm đường tăng luồng
int u = q.front(); q.pop();// Lấy đỉnh u tại đầu hàng đợi
for(int i=0; i<adj[u].size(); i++) { // Duyệt các đỉnh v kề với u: cung (u;v)
int v = adj[u][i]; // v kề u
if(trace[v] == -1){ // chưa thăm v, nên cung (u; v) là cung thuận
if (c[u][v] – f[u][v] > 0) { // Còn có thể tăng luồng trên cung (u;v)
trace[v] = u; // Đánh dấu đã thăm v, trước v là u
delta[v]= min(delta[u],c[u][v]-f[u][v]);// Luồng tối đa còn có thể qua v
if(v==t) return delta[t];// Nếu v là t thì c2[t] là giá trị luồng được tăng thêm
q.push(v); // còn không thì nạp v vào hàng đợi theo BFS
}
}
}
}
return 0; // Không còn tìm được đường tăng luồng, trả về giá trị luồng tăng thêm bằng 0
}
int EdmondsKarp() {
int maxFlow=0;
while(true) { // Vòng lặp thực hiện thuật toán EdmondsKarp tìm luồng cực đại
int flow=bfs();// giá trị luồng được tăng thêm khi tìm thấy đường tăng luồng
if(flow==0) break; // không còn đường tăng luồng thì kết thúc
maxFlow +=flow; // Cập nhật thêm cho giá trị luồng
int v = t; // Lần ngược đường tăng luồng để cập nhật luồng trên các cung
while( v != s){
int u = trace[v];
f[u][v] += flow; // Tăng luồng trên cung thuận
f[v][u] -= flow; // Giảm luồng trên cung đối
v= u;
}
}
return maxFlow;
}
int main() {
fi>> n>> m >> s >> t; // Đọc số đỉnh, số cạnh, đỉnh phát, đỉnh thu
for(int i=0; i< m; i++) { // Đọc m cung
int u, v, w;
fi>> u >> v >> w; // Cung (u;v) có sức chứa w
c[u][v]= w; // Ma trận sức chứa
adj[u].push_back(v); // Nạp v vào danh sách các đỉnh kề của u ( từ u đi ra)
adj[v].push_back(u); // Nạp u vào danh sách các đỉnh kề của v (đi vào v)
}
int maxFlow = EdmondsKarp();// Thuật toán trả về giá trị luồng cực đại
fo<<maxFlow<<endl; // Xuất ra output giá trị luồng cực đại
for (int u=0; u<n; u++) // Xuất ra luồng trên các cung
for (int v=0; v<n; v++)
if (f[u][v]>0)
fo << u << ” ” << v << ” ” << f[u][v] << endl;
return 0;
}
Chương trình 3. Tương tự chương trình 2 nhưng cài đặt đồ thị sức chứa và đồ thị luồng bằng danh sách cạnh (mảng E[2*maxM]) và danh sách các cung liên thuộc (mảng link[2*maxM] và mảng head[maxN]):
#include <iostream>
#include <fstream>
#include <queue>
#define maxN 100
#define maxM 1000
#define INF 2147483646
using namespace std;
ifstream fi (“MAXFLOW.INP”);
ofstream fo (“MAXFLOW.OUT”);
struct T_Edge { int x; int y; int c; int f; } E[2*maxM];
int link[2*maxM], head[maxN], trace[maxN];
int N, M, s, t;
void read_input() {
int j, u, v, capacity;
fi >> N >> M >> s >> t;
for (int i=1; i<=M; i++) {
fi >> u >> v >> capacity; // Cung (u;v) có sức chứa là capacity
E[i].x = u; E[i].y = v; // Cung thứ i có điểm đầu là u, điểm cuối là v
E[i].c = capacity; // Sức chứa của cung thứ i là capacity
link[i] = head[u]; // Đặt liên kết cung thứ i vào vị trí đầu của dslt của đỉnh u
head[u] = i; // Cung liên thuộc thứ nhất của đỉnh u là cung thứ i
j = 2*M+1 – i;
E[j].x = v; E[j].y = u; // Tương tự với cung –i là cung đối của cung i
E[j].c = 0;
link[j] = head[v];
head[v] = j;
}
fi.close();
for (int i=1; i<=2*M; i++) E[i].f=0; // Khởi trị luồng ban đầu trên các cung
}
int bfs(){
int delta[maxN];
queue<int> q;
memset(trace, -1, sizeof(trace));
memset(delta, INF, sizeof(delta));
q.push(s); // Nạp s vào hàng đợi
trace[s]=-2;
delta[s]= INF;
while(!q.empty()) {
int u = q.front(); q.pop();// Lấy đỉnh ở đầu của hàng đợi
int i = head[u]; // Cung liên thuộc đầu tiên của đỉnh u
while (i!=0) { // Còn cung i đi ra từ u
int v = E[i].y; // v là điểm cuối của cung i
if(trace[v] == -1){ // Đánh dấu đã thăm đỉnh v
if (E[i].c-E[i].f> 0) { // Trên cung i còn khả năng tăng luồng
trace[v] = i; // Lưu vết: v thuộc cung liên thuộc thứ i
delta[v]= min(delta[u],E[i].c-E[i].f); // Lương tăng luồng trên cung i
if(v==t) return delta[t]; // Tới đinht thu, trả về lượng tăng luồng trên cung tới t
q.push(v); // Còn không thì nạp v vào hàng đợi theo BFS
}
}
i=link[i]; // Cung liên thuộc tiếp theo (nếu i khác 0)
}
}
return 0;
}
int EdmondsKarp() {
int maxFlow=0;
while(true) {
int flow=bfs();// Trả về kết quả tìm đường tăng luồng
if(flow==0) break; // Nếu kết quả =0: không còn đường tăng luồng thì kết thúc
maxFlow +=flow; // Cập nhật giá trị luồng cực đại sau một lần tìm đường tăng luồng
int v = t; // Lần ngược đường tăng luồng để cập nhật luồng trên các cung
do {
int i = trace[v]; // Cung i là cung liên thuộc của v
E[i].f += flow; // Tăng luồng trên cung i (cung thuận)
E[2*M+1-i].f -= flow; // Giảm luồng trên cung đối của cung i là cung -i
v= E[i].x; // Lần ngược dần về phía đỉnh phát
} while( v != s);
}
return maxFlow;
}
int main() {
read_input();
int FlowValue = EdmondsKarp();
fo << FlowValue << endl;
for (int i=1; i<=M; i++)
if (E[i].f > 0)
fo << E[i].x << ” ” << E[i].y << ” ” << E[i].c << ” ” << E[i].f << endl;
return 0;
}
b) Thuật toán đẩy tiền luồng giải bài toán luồng cực đại
- Tiền luồng. Cho mạng G(V, E, c, s, t). Một tiền luồng (preflow) là hàm ánh xạ từ tập cung E vào tập số thực
f: E → ℝ
e→f(e)
thỏa mãn 3 ràng buộc:
+ Với mọi cung e∈E : f(e)≤c(e)
+ Với mọi cung e∈E : f(-e) = f(e) -e là cung đối của e
+ Tổng tiền luồng trên các cung e đi vào đỉnh v (v khác s) là số không âm, ký hiệu là excess[v]≥0. Khi excess[v]>0 thì v gọi là đỉnh tràn (nếu v khác s và t).
- Hàm độ cao h ứng với tiền luồng f trên mạng G(V, E, c, s, t). Đó là ánh xạ từ tập đỉnh V vào tập số tự nhiên
h: V→ℕ
v→h(v)
thỏa mãn 3 điều kiện (gọi là ràng buộc về độ cao):
+ h(s) = |V|
+ h(t) =0
+ h(u) ≤ h(v) +1 với mọi cung thặng dư (f(e)<c(e)), e=(u,v)
- Khởi trị trên mạng G = (V, E, s, t, c)
– Khởi trị tiền luồng: f(e)=c(e) với mọi cung đi ra từ s, f(e)=-c(e) nếu có cung –e đi vào s, f(e)=0 trong các trường hợp còn lại,
– Khởi trị hàm độ cao: h(s) = |V |, h(t)=0, còn lại h(v)=1 khi v khác s và t.
– Khởi tạo các giá trị exces[] ứng với tiền luồng f (mức tràn tại các đỉnh)
- Hàm đẩy luồng pushflow(e):
Trên cung thặng dư e=(u,v) nếu u là đỉnh tràn và u cao hơn v (cụ thể là h(u)=h(v)+1) thì thực hiện đẩy luồng: san bớt một lượng tràn thích hợp của u sang v. Lượng tràn này là ∆=Min{excess(u), cf(u,v)}. Khi đó luồng trên e tăng ∆, trên –e giảm ∆, độ tràn trên u giảm ∆, độ tràn trên v tăng ∆.
- Hàm nâng đỉnh Lift(u):
Với đỉnh u (khác s và t) là đỉnh tràn, nếu không thể chuyển luồng xuống các đỉnh thấp hơn (nghĩa là với mọi cung thặng dư e=(u,v) đều gặp h(u)≤h(v)) thì phải nâng độ cao của đỉnh u bằng độ cao đỉnh v thấp nhất cộng thêm 1.
- Mô hình thuật toán đẩy tiền luồng
Sau khi khởi tại tiền luồng và hàm độ cao, thực hiện hàm Lift(u) hoặc pushflow(e) cho đến khi không thực hiện được hai hàm này. Khi đó tiền luồng f thành luồng cực đại trên mạng
Thời gian trung bình thực hiện thuật toán là O(|E| · |V |2)
Ngày càng có nhiều cải tiến quá trình và thứ tự thực hiện hàm Lift(u) hoặc pushflow(e). Trong đó các thuật toán FIFO Preflow-push với tổ chức dữ liệu dưới dạng danh sách kề hoặc danh sách cung liên thuộc và dùng hàng đợi chứa các đỉnh tràn cùng với sử dụng các mẹo cài đặt (heuristics) khác nhau sẽ cho hiệu quả khác nhau. Nhưng nói chung thời gian thực hiện là O(|V|3 + |V|.|E|).
Với nhận xét: Nếu có số nguyên gap mà 0<gap<|V| mà không có đỉnh nào có độ cao bằng gap (gap gọi là “khe”) thì khi đó mọi đỉnh z có độ cao h(z)>gap sẽ không có đường đi trên các cung thặng dư tới t. Từ đó nảy sinh việc xây dựng hàm đẩy nhãn theo khe: Nếu có gap, mọi đỉnh z khác s có độ cao h[z] mà: gap<h(z)≤|V| sẽ được gán lại nhãn là h(z)=|V|+1. Việc nâng nhãn qua khe vẫn bảo đảm các ràng buộc tiền luồng và ràng buộc độ cao nhưng tạo điều kiện đẩy tiền luồng để cuối cùng không còn đỉnh tràn. Khi đó giá trị tiền luồng chính là giá trị luồng cực đại.
Dưới đây là chương trình thực hiện FIFO Preflow-push với heuristics đẩy nhãn qua khe: Trong thao tác đẩy nhãn qua khe, sử dụng mảng count_[2*M] để đếm số đỉnh có cùng độ cao.
#include <iostream>
#include <fstream>
#include <queue>
#define maxN 1000
#define maxM 100000
#define maxC 10000
using namespace std;
struct TEdge {long x,y,c,f; } E[2*maxM];
queue<int> Q;
long link[2*maxM];
long head[maxN], current[maxN];
long excess[maxN];
long h[maxN];
long count_[2*maxN];
long N, M, s, t, FlowValue;
ifstream fi (“PreFlowPush.inp”);
ofstream fo (“PreFlowPush.out”);
void read_input() {
long u, v, c, j;
fi >> N >> M >> s >> t;
for (int i=1; i<=M; i++){// Tạo danh sách các cung liên thuộc của mạng và tiền luồng
fi >> u >> v >> c;
E[i].x = u; E[i].y = v; E[i].c = c; // Đưa cung thuận (u,v) vào dslt của u
link[i] = head[u]; head[u] = i;
j = 2*M + 1 – i;
E[j].x = v; E[j].y = u; E[j].c = 0; // Thêm cung đối (v,u) vào dslt của v
link[j] = head[v]; head[v] = j;
}
// Khởi trị mảng curent[maxN] để biết cung liên kết đi ra khỏi v hiện thời là cung nào
for (int v=1;v<=N;v++) current[v]=head[v];
}
void Init() {
long i, sf, v;
for (long i=1; i<=2*M; i++) E[i].f=0; // Khởi trị tiền luồng 0
// Duyệt các cung đi ra khỏi s, đẩy bão hòa các cung đó, cập nhật mức tràn của s
i= head[s];
while (i!=0) {
sf = E[i].c; E[i].f = sf; E[2*M + 1 – i].f = -sf;
excess[E[i].y] += sf;
excess[s] -= sf;
i = link[i];
}
// Khởi trị độ cao các đỉnh
for (int v=1; v<=N; v++) h[v]=1;
h[s]=N; h[t]=0;
// Khởi trị mảng đếm số độ cao bằng nhau
count_[N]=1; count_[0]=1; count_[1]=N-2;
// Nạp các đỉnh tràn vào hàng đợi Q
for (int v=1; v<=N; v++)
if ((v!=s)&&(v!=t)&&(excess[v]>0)) Q.push(v);
}
void Push(long i) { // Hàm đẩy tiền luồng trên cung i
long delta;
// Lượng đẩy
if (excess[E[i].x]<E[i].c-E[i].f) delta=excess[E[i].x];
else delta =E[i].c-E[i].f;
// Thực hiện đẩy luồng trên cung thuận và cung đối, cập nhật mức tràn tại hai đầu cung
E[i].f += delta;
E[2*M+1-i].f -=delta;
excess[E[i].x] -= delta;
excess[E[i].y] += delta;
}
void SetH(int u, int NewH) {// Cập nhật độ cao mới cho đỉnh u, đồng thời cập nhật count_
count_[h[u]]–; h[u] = NewH;
count_[NewH]++;
}
void GapHeuristic(int gap) {// Tìm khe gap và đẩy nhãn h qua khe
int v;
if ((0<gap) && (gap<N) && (count_[gap]==0)) // Điều kiện của gap
for (int v=1; v<=N; v++)
if ((v!=s) && (gap<h[v]) && (h[v]<=N)) { // Điều kiện đỉnh v được đẩy nhãn
SetH(v, N+1); // Thực hiện đẩy nhãn cho v
current[v] = head[v]; // Lưu cung hiện thời đi ra khỏi v
}
}
void Lift(int u) { // Hàm nâng nhãn h cho đỉnh u
long minH, OldH, i;
minH= 2* maxN;
i = head[u]; // Duyệt các cung (u,v) thặng dư có h[v] thấp nhất lưu vào minH
while (i!=0) {
if ((E[i].c >E[i].f) && (h[E[i].y]<minH) )
minH = h[E[i].y];
i = link[i];
}
OldH = h[u]; // Lưu độ cao cũ của u
SetH(u, minH+1); // Cập nhật độ cao mới cho u, trong đó có giảm count_[OldH]
GapHeuristic(OldH); // Kiểm tra độ cao cũ có trở thành “gap”, và xử lý
}
void FIFOpreflowpush() { // Thuật toán FIFO đẩy tiền luồng dùng đẩy nhã qua khe
bool NeedQ; int z; long i;
while (Q.size()>0) { // Trong khi còn đỉnh tràn, còn thực hiện
z = Q.front(); Q.pop();// Lấy từ hàng đợi đỉnh tràn z
while (current[z]!=0) { // Trong khi còn cung hiện thời đi ra từ z
i= current[z]; // cung i đi ra từ z
if ((E[i].c>E[i].f)&&(h[E[i].x]>h[E[i].y])){//có thể đẩy luồng trên i
// Ghi nhận lạ đỉnh cuối cung i có tràn hay không
NeedQ=((E[i].y!=s)&&(E[i].y!=t)&&(excess[E[i].y]==0));
// Đẩy luồng theo cung i, làm z bớt tràn dần
Push(i);
// Nếu đỉnh cuối cung i là đỉnh không tràn thì sau Push(i) nó thành đỉnh tràn
if (NeedQ) {
Q.push(E[i].y); // Do đó phải nạp nó vào hàng đợi
if (excess[z]==0) break; // z hết tràn thì chuyển qua xét điểm khác
}
}
current[z] = link[i]; // z chưa hết tràn thì xét sang đỉnh khác
}
// Đã xét hết các đỉnh kề với z mà z vẫn là đỉnh tràn thì phải nâng nhãn cho z
if (excess[z]>0) {
Lift(z); // Nâng nhãn cho z
current[z]=head[z]; // đặt cung hiện thời đi ra từ z từ đầu dslt
Q.push(z); // Nạp lại z vào hàng đợi chứa các đỉnh tràn
}
}
// Đã xử lý hết các đỉnh tràn, giá trị tiền luồng là giá trị luồng cực đại
FlowValue = excess[t];
}
int main() {
read_input();
Init();
FIFOpreflowpush();
fo << FlowValue << endl;
for (long i=1; i<=M ; i++) // Hiện các cung có luồng dương
if (E[i].f>0)
fo << E[i].x << ” ” << E[i].y << ” ” << E[i].c << ” ” << E[i].f << endl;
return 0;
}
Chia sẻ:
- X
Từ khóa » Ví Dụ Về Luồng Cực đại
-
Luồng Cực đại – Wikipedia Tiếng Việt
-
Luồng Cực đại Trên Mạng - Maxflow Network - VNOI
-
[PDF] Luồng Cực đại
-
(DOC) Luồng Cực đại | Trần Lanh
-
Bài Toán Luồng Cực đại Trong Mạng - Tài Liệu Text - 123doc
-
Luồng Cực đại Và Một Số Bài Toán ứng Dụng - Tài Liệu Text - 123doc
-
Giải Thuật Và Lập Trình: §10. Bài Toán Luồng Cực đại Trên Mạng | V1Study
-
Luong Cuc Dai Va Mot So Bai Toan Ung Dung - PHẦẦN I - StuDocu
-
Định Lý Luồng Cực đại Lát Cắt Cực Tiểu - Du Học Trung Quốc
-
Tài Liệu CHƯƠNG 2: BÀI TOÁN LUỒNG CỰC ĐẠI
-
Định Lý Luồng Cực đại Lát Cắt Cực Tiểu - Wikiwand
-
MAX FLOW (Luồng Cực đại) | Hướng Dẫn Giải Tay - YouTube
-
Lý Thuyết đồ Thị - Bài Toán Luồng Trong Mạng, Luồng Cực đại (bài 9)
-
Luồng Và Lát Cắt (Flows And Cuts) - Vallicon