1. Trang chủ
  2. » Giáo Dục - Đào Tạo

Môn tin học bài toán luồng cực Đại thuật toán ford – fulkerson và những cải tiến

88 11 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Bài Toán Luồng Cực Đại – Thuật Toán Ford – Fulkerson Và Những Cải Tiến
Trường học Trường THPT Chuyên Khu Vực Duyên Hải
Chuyên ngành Tin Học
Thể loại bài báo
Năm xuất bản 2021
Thành phố Duyên Hải
Định dạng
Số trang 88
Dung lượng 6,41 MB

Cấu trúc

  • 1. Giới thiệu (5)
  • 2. Một số khái niệm (5)
    • 2.1. Mạng (5)
    • 2.2. Luồng trên mạng (6)
    • 2.3. Hình ảnh thực tế (7)
    • 2.4. Bài toán luồng cực đại tổng quát (7)
  • 3. Thuật toán Ford – Fulkerson và những cải tiến (0)
    • 3.1. Thuật toán gốc và một số khái niệm (8)
      • 3.1.1. Ý tưởng thuật toán (8)
      • 3.1.2. Mạng thặng dư (Residual Network) (8)
      • 3.1.3. Ví dụ Minh họa thuật toán Ford – Fulkerson (10)
      • 3.1.4. Độ phức tạp (14)
      • 3.1.5. Bài toán Luồng cực đại cụ thể (14)
      • 3.1.6. Link tải bộ test (15)
    • 3.2. Cài đặt thuật toán Ford – Fulkerson và những thuật toán cải tiến (15)
      • 3.2.1. Thuật toán Ford – Fulkerson phiên bản gốc (15)
      • 3.2.2. Thuật toán Edmonds – Karp: Shortest path (16)
      • 3.2.3. Thuật toán Edmonds – Karp: Fattest Path (20)
      • 3.2.4. Thuật toán Edmonds – Karp, Dinitz: Capacity scaling (22)
      • 3.2.5. Thuật toán Dinic (25)
      • 3.2.6. Bảng tổng hợp các thuật toán (29)
    • 3.3. Lát cắt (30)
      • 3.3.1. Khái niệm (30)
      • 3.3.2. Khả năng thông qua của lát cắt (30)
      • 3.3.3. Bài toán tìm lát cắt nhỏ nhất (30)
  • 4. Bài tập ứng dụng (34)
    • 4.1. AFINDFLOW (34)
      • 4.1.1. AFINDFLOW_SOLUTION (36)
      • 4.1.2. Độ phức tạp (36)
      • 4.1.3. Code tham khảo (36)
      • 4.1.4. Link tải bộ test (38)
    • 4.2. BDISJCON (38)
      • 4.2.1. BDISJCON _SOLUTION (40)
      • 4.2.2. Độ phức tạp (41)
      • 4.2.3. Code tham khảo (41)
      • 4.2.4. Link tải bộ test (42)
    • 4.3. CSCHEWAR (43)
      • 4.3.1. CSCHEWAR_SOLUTION (44)
      • 4.3.2. Độ phức tạp (44)
      • 4.3.3. Code tham khảo (44)
      • 4.3.4. Link tải bộ test (46)
    • 4.4. CBADGES (47)
      • 4.4.2. Chứng minh (49)
      • 4.4.3. CBADGES_SOLUTION (49)
      • 4.4.4. Độ phức tạp (50)
      • 4.4.5. Code tham khảo (50)
      • 4.4.6. Link tải bộ test (52)
    • 4.5. CODSPORTS (54)
      • 4.5.1. CODSPORTS_SOLUTION (55)
      • 4.5.2. Độ phức tạp (56)
      • 4.5.3. Code tham khảo (56)
      • 4.5.4. Link tải bộ test (59)
    • 4.6. CEDGEDES (59)
      • 4.6.1. CEDGEDES_SOLUTION (61)
      • 4.6.2. Độ phức tạp (61)
      • 4.6.3. Code tham khảo (61)
      • 4.6.4. Link tải bộ test (64)
    • 4.7. DTELETOWER (65)
      • 4.7.1. DTELETOWER_SOLUTION (66)
      • 4.7.2. Định lý Konig (66)
      • 4.7.3. Độ phức tạp (68)
      • 4.7.4. Code tham khảo (68)
      • 4.7.5. Link tải bộ test (71)
    • 4.8. DGRIDS (72)
      • 4.8.1. DGRIDS_SOLUTION (73)
      • 4.8.2. Độ phức tạp (75)
      • 4.8.3. Code tham khảo (75)
      • 4.8.4. Link tải bộ test (77)
    • 4.9. ECOVERCHESS (78)
      • 4.9.1. ECOVERCHESS_SOLUTION (79)
      • 4.9.2. Độ phức tạp (85)
      • 4.9.3. Code tham khảo (85)
      • 4.9.4. Link tải bộ test (87)
  • 5. Một số bài tập luyện tập (87)
  • 6. Tài liệu tham khảo (87)
  • 7. Kết luận (88)

Nội dung

Để học phần này, học sinh cần được trang bị một số kiến thức như là: Đồ thị cơ bản, duyệt trên đồ thị với DFS và BFS, tìm đường đi ngắn nhất trên đồ thị, xử lý đồ thị dạng mảng 2 chiều,

Giới thiệu

Bài toán Luồng cực đại, dù không mới, vẫn là một nội dung khó đòi hỏi tư duy hình tượng cao Để giải quyết bài toán này, học sinh cần nắm vững kiến thức về đồ thị cơ bản, duyệt đồ thị (DFS, BFS), tìm đường đi ngắn nhất, xử lý đồ thị mảng 2 chiều (kỹ thuật duỗi mảng), và các cấu trúc dữ liệu như priority_queue, vector.

Bài viết giới thiệu thuật toán Ford-Fulkerson và các thuật toán cải tiến quan trọng như Edmonds-Karp (đường ngắn nhất, đường dày nhất, điều chỉnh dung lượng) cùng Dinic, giúp giải quyết hiệu quả bài toán luồng cực đại trên cấu hình lớn.

Chuyên đề này tổng hợp các nghiên cứu về bài toán luồng cực đại, trình bày các bài toán liên quan như lát cắt cực tiểu (Min-Cut) và cặp ghép cực đại (Maximum Matching) trên đồ thị hai phía, cùng các định lý quan trọng như Konig và Menger.

Chuyên đề hướng đến đối tượng là học sinh giỏi lớp 11, ôn tập chuẩn bị tham gia kỳ thi học sinh giỏi quốc gia hàng năm.

Chuyên đề gồm hệ thống bài tập 5 cấp độ (A-E): cơ bản, trung bình, khá, khó, rất khó, thể hiện qua ký tự đầu tên bài Giáo viên tận dụng cấp độ này để phân bổ bài tập phù hợp cho học sinh.

Chuyên đề này cung cấp kiến thức hữu ích và cần thiết cho quý thầy cô đồng nghiệp tham dự hội thảo Nội dung chi tiết được trình bày dưới đây.

Một số khái niệm

Mạng

Là một đồ thị có hướng G = (V,E) Trong đó:

 Có duy nhất một đỉnh s chỉ có cung đi ra (đỉnh phát – Source).

 Có duy nhất một đỉnh t chỉ có cung đi vào (đỉnh thu - Sink).

 Các cung e được gán với một giá trị c(e) ≥ 0 gọi là khả năng thông qua(Capacity) của cung e.

Luồng trên mạng

Cho mạng G(V,E) Luồng f trong mạng G là một cách gán cho mỗi cung e một số thực (các bài toán ta xét thường là số nguyên) không âm f(e)

 f(e) gọi là luồng trên cung e.

 f(e) phải thỏa các điều kiện sau: a Luồng trên mọi cung phải nhỏ hơn khả năng thông qua của cung đó (

1≤ f(e)≤ c(e)) b Tổng luồng trên các cung đi vào (inflow) đỉnh v (v ≠ s, t) bằng tổng luồng trên các cung đi ra (outflow) khỏi v.

( v , z )∈ E f(v , z) c Giá trị của luồng f là số val(f) với val(f)= ∑

Hình ảnh một luồng (f(u,v) = 6) trên cung (u,v) có khả năng thông qua c(u,v) 17

Hình ảnh về một mạng với luồng cực đại Nhìn hình trên ta thấy:

 Trên mỗi cạnh e, tồn tại 1 luồng f(e) ≤ c(e)

 Tại mọi đỉnh v ∈ V – {s,t}, ta đều thấy inflow tại v = outflow tại v = 10

 Giá trị val(f*) = outflow tại s = inflow tại t = 25

Hình ảnh thực tế

Ví dụ: Nước chảy từ nguồn chứa đến bồn rửa thông qua các ống nước.

Khả năng thông qua của mỗi cung mạng tương tự như dung lượng tối đa của một ống dẫn nước, trong khi lưu lượng thực tế là lượng dữ liệu truyền qua Lưu lượng không thể vượt quá khả năng thông qua.

Các đỉnh mạng lưới đóng vai trò điểm nối, phân phối nước chảy vào các ống khác Tổng lượng nước chảy ra từ mỗi điểm nối bằng tổng lượng nước chảy vào, tuân thủ định luật bảo toàn.

Đỉnh s là nguồn nước (Source), trong khi đỉnh t là nơi nước thoát ra (Sink), thỏa mãn điều kiện c.

Bài toán luồng cực đại tổng quát

Cho mạng G(V,E) Hãy tìm luồng f* trong mạng G sao cho giá trị val(f*) của luồng f* là lớn nhất

Lưu ý: Từ đây ta dùng ký hiệu f* để chỉ luồng cực đại trên mạng

Thuật toán Ford – Fulkerson và những cải tiến

Thuật toán gốc và một số khái niệm

Được đề xuất bởi L R Ford và D R Fulkerson năm 1956

1/ Bắt đầu với khởi tạo luồng f* = 0

2/ Trong khi , ta thực hiện tăng luồng f dọc theo đường tăng luồng tìm được.

3/ Trả về giá trị luồng f*

3.1.2.Mạng thặng dư (Residual Network)

Là mạng Gf có số đỉnh là V, số cung là 2*E Gf cho biết khả năng có thể tăng thêm cho luồng f trên mỗi cung Một số đặc điểm

Trong mạng thặng dư, mỗi cung e(u,v) có cung ngược e’(v,u) và một giá trị thặng dư RC(e) thể hiện khả năng tăng thêm lưu lượng f(e) trên cung e, được tính toán tương ứng.

Ví dụ: Cung e(u,v) như hình sau

Trong mạng thặng dư tương ứng, ta có cung thuận e(u,v), và cung ngược e’(v,u) lần lượt có RC(e) = c(e) – f(e) = 17 – 6 = 11, RC(e’) = f(e) = 6 như sau:

Trên mạng thặng dư, nếu tồn tại đường đi từ nguồn s đến đích T với tất cả các cung có giá trị dung lượng dư RC(e) > 0, thì đó là đường tăng luồng.

 Nếu không còn tìm được đường tăng luồng thì khi đó luồng là cực đại.

 Để tìm đường tăng luồng ta có thể sử dụng các thuật toán DFS, BFS, PFS (Priority First Search) trên mạng thặng dư

 Nếu gọi B là giá trị thặng dư thông qua nhỏ nhất trên đường tăng luồng tìm được (hay còn gọi B là “bottleneck capacity” theo một số sách)

1 https://www.geeksforgeeks.org/ford-fulkerson-algorithm-for-maximum-flow-problem/

Thuật toán cập nhật luồng trên đường tăng luồng tìm được bằng cách tăng luồng trên mỗi cung (u,v) một lượng B = min(RC(e)) và giảm luồng trên cung ngược (v,u) cùng lượng B đó, với e là cung trên đường tăng luồng.

Câu hỏi: Tại sao lại tồn tại cung ngược trên mạng thặng dư?

Ta xét một trường hợp mạng G và đường tăng luồng f là các cung màu xanh như sau:

Bài toán tìm luồng cực đại cho đồ thị đã đạt giới hạn với giá trị |f| = 2, sử dụng 2 đường tăng luồng Không thể tăng luồng thêm nữa theo phương pháp đã sử dụng.

Vậy: Có cách nào giúp ta giảm luồng f (trên mỗi cung) đi một lượng bằng với lượng đã tăng trước đó hay không?

 Câu trả lời chính là ta sẽ thêm mỗi cung trong mạng thặng dư một cung ngược (backward edge) tương ứng

Cụ thể trong trường hợp chọn sai đường tăng luồng như hình đầu Ta có mạng thặng dư khi đã thêm cung ngược như sau:

Tiếp theo, ta có thể chọn đường tăng luồng như đường màu xanh lá trong hình sau:

Kết quả ta có quá trình tăng luồng và kết quả như sau:

Hình thứ 3 là kết quả sau khi tăng luồng f lần thứ 2 Và khi này luồng f đã đạt cực đại là 2

 Kết luận: Các cung ngược giúp ta có thể giảm luồng f(e) đi 1 lượng đã tăng trước đó Trong trường hợp này là cung (u,v)

3.1.3.Ví dụ Minh họa thuật toán Ford – Fulkerson

Cho mạng G(V,E) như hình sau:

Ta có mạng thặng dư, đường tăng luồng là: s, 2, 5, t  B = 8  f = 0 + B = 0 + 8 = 8.

Ta có mạng thặng dư, đường tăng luồng là: s, 2, 3, 5, t  B = 2  f = 8 + 2 10

Ta có mạng thặng dư, đường tăng luồng là: s, 3, 5, 4, t  B = 6  f = 10 + 6 16

Ta có mạng thặng dư, đường tăng luồng là: s, 3, 2, 4, t  B = 2  f = 16 + 2 18

Ta có mạng thặng dư, đường tăng luồng là: s, 3, 5, 2, 4, t  B = 1  f = 18 + 1

Ta thấy không thể tìm thấy đường tăng luồng trên mạng thặng dư G f

 Luồng đã đạt giá trị cực đại |f*| = 19

Thuật toán Ford-Fulkerson tìm đường tăng luồng bằng DFS hoặc BFS với độ phức tạp O(E), lặp lại quá trình này nhiều lần cho đến khi đạt luồng cực đại f* Độ phức tạp tổng thể là O(E|f*|) Để tối ưu, ta cần giảm số lượng đường tăng luồng hoặc số lần lặp, vấn đề sẽ được thảo luận chi tiết hơn.

3.1.5.Bài toán Luồng cực đại cụ thể

Cho mạng G có n đỉnh và m cạnh, đỉnh phát là 0, và đỉnh thu là n-1 Hãy tìm luồng f* trong mạng sao cho giá trị val(f*) của luồng f* là lớn nhất

 Dòng 1: Ghi 2 số nguyên n, m cho biết số đỉnh và số cung trong mạng G.

 m dòng tiếp theo: Mỗi dòng ghi 3 số nguyên u, v, c cho biết cung nối từ u đến v có trọng số là c

 Ghi một số nguyên dương cho biết giá trị luồng cực đại tìm được

Giải thích: Ta có luồng trên mạng trong test mẫu là:

3.1.6.Link tải bộ test https://drive.google.com/drive/folders/1AHLoTEK0sAWbD42U1xU6zbvkLLfjw3WT?usp=sharing

Cài đặt thuật toán Ford – Fulkerson và những thuật toán cải tiến

 Gọi par[v]: lưu trữ đỉnh u là cha trực tiếp của v Khởi tạo ban đầu các par[u] -1 (u≠s), riêng par[s] = 0

 Gọi a[u]: lưu trữ danh sách các đỉnh kề với u.

 Gọi RC[u][v]: Giá trị khả năng thặng dư thông qua trên mạng thặng dư của cung (u,v) Khởi tạo ban đầu với các cung (u,v) ta có: RC[u][v] = C[u][v], RC[v][u] = 0;

Thuật toán DFS tìm đường tăng luồng đơn giản khi cài đặt nhưng thường cần nhiều lần tăng luồng, dẫn đến thời gian xử lý chậm hơn so với BFS.

#include using namespace std;

#define N 205 vector adj[N]; int RC[N][N]; int par[N],n,m,s,t, add_flow;

{ int u,v,c; cin>>u>>v>>c; adj[u].push_back(v); adj[v].push_back(u);

{ for(auto v:adj[u]) if(par[v] ==-1 && RC[u][v])

{ par[v] = u; add_flow = min(add_flow,RC[u][v]); dfs(v);

// - int Maxflow(int s, int t, int n)

{ for (int i = 1 ; i >v>>c; adj[u].push_back(v); adj[v].push_back(u);

// - bool bfs(int s, int t, int n)

{ for (int i = 1 ; i q; q.push(s); while (!q.empty())

{ int u = q.front(); q.pop(); for (auto v:adj[u]) if (par[v] == -1 && RC[u][v])

{ par[v] = u; add_flow = min(add_flow,RC[u][v]); if (v == t) return true; q.push(v);

// - int Maxflow(int s, int t, int n)

{ flow += add_flow; int v = t; while(v!=s)

{ freopen("AMAXFLOW.INP","r",stdin); freopen("AMAXFLOW.OUT","w",stdout); ios_base::sync_with_stdio(false); cin.tie(0); nhap(); coutu>>v>>c; adj[u].push_back(v); adj[v].push_back(u);

// - bool pfs(int s, int t,int n)

{ int Bmax[n+1]={0}; for (int i = 0 ; i 2); x = x| (x>>4); x = x| (x>>8); x = x| (x>>16); return (x+1)>>1;

{ int u,v,c; cin>>u>>v>>c; adj[u].push_back(v); adj[v].push_back(u);

// - bool bfs(int s, int t, int n)

{ for (int i = 1 ; i q; q.push(s); while (!q.empty())

{ int u = q.front(); q.pop(); for (auto v:adj[u])

//Xét các cạnh trên mạng thặng dư có giá trị RC(e) từ delta trở lên if (par[v] == -1 && RC[u][v]>ta)

{ par[v] = u; add_flow = min(add_flow,RC[u][v]); if (v == t) return true; q.push(v);

// - int Maxflow(int s,int t, int n)

{ flow += add_flow; int v = t; while(v!=s)

{ freopen("AMAXFLOW.INP","r",stdin); freopen("AMAXFLOW.OUT","w",stdout); ios_base::sync_with_stdio(false); cin.tie(0); nhap(); coutu>>v>>c; adj[u].push_back(v); adj[v].push_back(u);

// Kiểm tra xem có thể tăng thêm luồng được không?

// Xây dựng level graph bool BFS(int s, int t, int n)

{ for (int i = 1 ; i q; q.push(s); while (!q.empty())

{ int u = q.front(); q.pop(); for(auto v:adj[u]) if (level[v] < 0 && RC[u][v])

//Kết quả kiểm tra có tăng thêm luồng được không? return level[t] < 0 ? false : true;

Hàm `sendFlow`, hoạt động tương tự thuật toán DFS, gửi luồng trên đồ thị thặng dư sau khi BFS xác định khả năng tăng luồng và xây dựng đồ thị mức (level graph).

// cho đến khi đạt được blocking flow thì dừng

// Các tham số hàm gồm: int sendFlow(int u, int flow)

//Duyệt qua lần lượt từng cạnh kề với u, tăng số lượng cạnh đã duyệt for(auto v:adj[u])

// Lấy cạnh kề tiếp theo trong danh sách kề của u if (level[v] == level[u]+1 && RC[u][v])

Lát cắt

Lát cắt (A,B) của mạng G = (V,E) là một cách phân hoạch tập V thành hai tập A và B=V\A Trong đó: s ϵ A ,t ϵ B

3.3.2.Khả năng thông qua của lát cắt

Khả năng thông qua của lát cắt st-A,B là giá trị: w ∑ ∈ B ¿ ¿ v ∈ A ¿ ¿

Ví dụ: Với s = 1, t = 7 và mạng G(V,E) Xét lát cắt (A,B), với A={1,2,3},

B={4,5,6,7}, ta có hình minh họa sau:

Khả năng thông qua của lát cắt trên là: cap ( A , B )= c ( 3 , 5 )+ c ( 3 , 6 )+ c ( 3 , 4 ) ==> c ( A , B )= 13 + 1 + 3 = 17

3.3.3.Bài toán tìm lát cắt nhỏ nhất

Bài toán tìm lát cắt nhỏ nhất trên mạng G nhằm xác định phân hoạch tập đỉnh V thành hai tập A và B sao cho dung lượng của lát cắt (cap(A,B)) là nhỏ nhất.

Ta quan sát một số ví dụ về lát cắt sau:

Ví dụ 3: A = {s, 3, 4, 7}, B = {2, 5, 6, t}  cap(A,B) = 28. Định lý 1:

Trong mạng G, với luồng f và lát cắt (A,B), tổng luồng qua lát cắt bằng tổng luồng ra khỏi nguồn s và bằng tổng luồng vào đích t, được biểu diễn bởi: ∑e ra khỏi A f(e) - ∑e đi vào A f(e) = ∑e ra khỏi s f(e) = ∑e đi vào t f(e) = val(f).

Thật vậy, ta quan sát vài ví dụ sau:

Ví dụ 1: A = {s}, B = {2, 3, 4, 5, 6, 7, t}  Theo định lý 1, ta có: val(f) = tổng luồng trên các cạnh ra khỏi A – tổng luồng trên các cạnh vào A= (10 + 4 + 10)

Ví dụ 2: A = {s, 2, 3, 4}, B = {5, 6, 7, t}  Theo định lý 1, ta có: val(f) = tổng luồng trên các cạnh ra khỏi A – tổng luồng trên các cạnh vào A= (6+0+8+10) –

Ví dụ 3: A = {s, 3, 4, 7}, B = {2, 5, 6, t}  Theo định lý 1, ta có: val(f) = tổng luồng trên các cạnh ra khỏi A – tổng luồng trên các cạnh vào A= (10+8+10) – (4+0) = 24

Ta sẽ chứng minh bằng qui nạp biểu thức: e ra khỏi A ∑ f(e)−¿ ∑ e đi vào A f (e)=val(f)¿

 Biểu thức đúng với trường hợp cơ sở A = {s}

 Giả sử biểu thức đúng với trường hợp |A| < k

 Ta sẽ chứng minh biểu thức cũng đúng với trường hợp |A| = k.

 Xét lát cắt (A,B) với |A| = k Ta có: A = A’ ∪ {v}, với v ≠ s,t; |A’| = k-1 và thỏa định lý 1

 Khi ta thêm đỉnh v vào tập A’, thì tổng lưu lượng luồng tăng thêm là: e ra khỏi v ∑ f (e)−¿ ∑ e đi vào v f(e)=0(∗)¿

(*) là theo khái niệm luồng đã trình bày ở đầu chuyên đề.

 Kết luận: rõ ràng lưu lượng luồng qua lát cắt không tăng thêm khi thêm v

Nên vẫn thỏa định lí 1 khi |A| = k. Định lý 2:

Cho f là một luồng trên mạng G Gọi (A,B) là một lát cắt trên G Thì ta có: val(f) ≤ cap(A,B)

Val(f)= ∑ e ra khỏi A f(e)− ∑ e đi vào A f(e)≤ ∑ e ra khỏi A f(e)≤ ∑ e ra khỏi A c(e)≤ cap(A , B)

Cho f là một luồng trên mạng G Gọi (A,B) là một lát cắt trên G Nếu val(f) cap(A,B), thì f đạt cực đại và lát cắt (A,B) là lát cắt nhỏ nhất

 Điều này có nghĩa là để giải bài toán tìm lát cắt nhỏ nhất, ta sẽ đi giải bài toán tìm luồng cực đại đã trình bày ở trên.

Khi luồng đạt cực đại, các đỉnh nguồn (s) trên đồ thị thặng dư có đường đi đến đỉnh đích (t) sẽ nằm trong tập đỉnh A của lát cắt tối thiểu Các đỉnh còn lại, bao gồm cả đỉnh đích (t), thuộc tập đỉnh B.

Trong ví dụ trên, ta thấy

 Tập A ={s,3} vì 3 là đỉnh đến được từ s trên đồ thị thặng dư sau khi f đạt cực đại.

Bài tập ứng dụng

AFINDFLOW

Cho một mạng G có n đỉnh và m cung Mỗi cung e của G có một khả năng thông qua c(e) Mạng G được mô tả như sau:

 Chỉ có một đỉnh phát ký hiệu là ký tự ‘S’, và một đỉnh thu ký hiệu là ‘T’.

 Các đỉnh khác S, T được ký hiện bởi một chữ cái in hoa từ ‘A’  ‘O’

 G không có các đỉnh kết thúc khác ngoài T

 Giữa mọi cặp đỉnh chỉ có một cung duy nhất

 Dòng 1: Ghi số nguyên m cho biết số lượng cung trên G

 m dòng tiếp theo: Mỗi dòng ghi 3 giá trị u, v, c cho biết có cung nối từ u đến v có giá trị thông qua là c Trong đó u,v là các ký tự hoa từ ‘A’  ‘O’

 Ghi một số nguyên duy nhất là luồng cực đại tìm được

4.1.1.AFINDFLOW_SOLUTION Đây là bài toán giúp học sinh ôn tập lại code cơ bản của luồng cực đại Với mỗi đỉnh, học sinh chuyển sang dạng số nguyên sau đó code tương tự các thuật toán đã trình bày ở trên

Lưu ý: Giáo viên có thể yêu cầu học sinh code theo nhiều thuật toán để kiểm chứng.

Với test mẫu, ta sẽ có mô hình mạng G như sau:

Luồng trên mạng G có val(f*) = 14:

Trong bài toán này tôi dùng thuật toán “Fattest path”  O(E 2 log(V)ln(|f*|))

#include using namespace std; typedef pair node; vector adj[1001]; int RC[1001][1001]; int par[1001],n,m,s,t,add_flow;

{ int u,v,c; string a,b; cin>>a>>b>>c; u = (a=="S")?16:(a[0]-65+1); v = (b=="T")?17:(b[0]-65+1); adj[u].push_back(v); adj[v].push_back(u);

// - bool pfs(int s, int t,int n)

{ int Bmax[n+1]={0}; for (int i = 0 ; i >v; adj[u].push_back(v); adj[v].push_back(u);

// - bool BFS(int s, int t, int n)

{ for (int i = 1 ; i q; q.push(s); while (!q.empty())

{ int u = q.front(); q.pop(); for(auto v:adj[u]) if (level[v] < 0 && RC[u][v])

// - int sendFlow(int u, int flow)

{ if (u == t) return flow; for(auto v:adj[u])

{ int curr_flow = min(flow, RC[u][v]); int add_flow = sendFlow(v, curr_flow); if (add_flow > 0)

RC[u][v] -= add_flow; return add_flow;

// - int DinicMaxflow(int s, int t, int n)

{ if (s == t) return -1; int flow = 0; while (BFS(s, t, n)) while (int add_flow = sendFlow(s, INT_MAX)) flow += add_flow; return flow;

{ freopen("BDISJCON.INP","r",stdin); freopen("BDISJCON.OUT","w",stdout); ios_base::sync_with_stdio(false); cin.tie(0); nhap(); int ans = DinicMaxflow(s,t,n); cout b thì ta hoán đổi giá trị a, b

 Theo bài toán cặp ghép cực đại đã tìm hiểu ở trên, thì kết quả bài toán là giá trị luồng cực đại trên mạng G’

Theo đó với test mẫu ta có mạng G’ như sau:

Luồng cực đại trên mạng G’ có val(f*) = 4 như hình sau:

Bài toán sử dụng thuật toán Dinic tìm cặp ghép cực đại trên đồ thị 2 phía, tương đương trên Unit Network  O(E√ V )

#include using namespace std; typedef pair node; vector adj[2050];

Các hv màu xanh int RC[2050][2050]; int par[2050],cnt[3][1050], level[2050],n,N,m,s,t,add_flow;

// - bool BFS(int s, int t, int n)

{ for (int i = 0 ; i q; q.push(s); while (!q.empty())

{ int u = q.front(); q.pop(); for(auto v:adj[u]) if (level[v] < 0 && RC[u][v])

// - int sendFlow(int u, int flow)

{ if (u == t) return flow; for(auto v:adj[u])

{ int curr_flow = min(flow, RC[u][v]); int add_flow = sendFlow(v, curr_flow); if (add_flow > 0)

RC[u][v] -= add_flow; return add_flow;

// - int DinicMaxflow(int s, int t, int n)

{ if (s == t) return -1; int flow = 0; while (BFS(s, t, n)) while (int add_flow = sendFlow(s, INT_MAX)) flow += add_flow; return flow;

// - bool check(int a, int b) { if (a < b) { swap(a, b);} return 2 * b * b > a * a;

// - void themcung(int u, int v, int c)

{ adj[u].push_back(v); adj[v].push_back(u);

// - void solve() { cin >> n; for (int i = 1; i > c >> d; cnt[c][d]++;

N = 1005; s = 2*N + 1; t = s+1; for (int i = 1; i

Ngày đăng: 26/11/2024, 14:27

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w