AlgoVerse

بیشینه‌ی جریان (ادموندز–کارپ)

تا وقتی در شبکه‌ی باقی‌مانده مسیری از مبدأ به آخرین گره هست، کوتاه‌ترینش را با BFS پیدا می‌کند و جریان را از آن عبور می‌دهد.

هر یال یک ظرفیت دارد. می‌خواهیم بیشترین جریان ممکن را از مبدأ (اینجا گره‌ی انتخاب‌شده) به مقصد (آخرین گره) بفرستیم، بی‌آنکه از ظرفیت هیچ یالی بیشتر شود و هر چه به گره‌ای وارد می‌شود از آن خارج شود.

ادامه‌ی توضیح ↓
ورودی:
V=6 · E=9 · src=Sخروجی PDF

شبکه‌ی شش‌گره‌ای مشهور با مبدأ S و مقصد T؛ بیشینه‌ی جریان ۲۳ است.

  1. from collections import deque
  2. INF = float("inf")
  3. def edmonds_karp(graph, s):
  4. t = list(graph)[-1] # the last node is the sink
  5. cap = {u: {} for u in graph}
  6. for u in graph:
  7. for v, w in graph[u]:
  8. cap[u][v] = cap[u].get(v, 0) + w
  9. cap[v].setdefault(u, 0)
  10. total = 0
  11. while True:
  12. parent = {v: None for v in graph}
  13. seen = {s}
  14. queue = deque([s])
  15. while queue and t not in seen:
  16. u = queue.popleft()
  17. for v in cap[u]:
  18. if cap[u][v] == 0 or v in seen:
  19. continue
  20. seen.add(v)
  21. parent[v] = u
  22. queue.append(v)
  23. if t not in seen:
  24. return total
  25. bottleneck = INF
  26. v = t
  27. while v != s:
  28. bottleneck = min(bottleneck, cap[parent[v]][v])
  29. v = parent[v]
  30. v = t
  31. while v != s:
  32. u = parent[v]
  33. cap[u][v] = cap[u][v] - bottleneck
  34. cap[v][u] = cap[v][u] + bottleneck
  35. v = u
  36. total = total + bottleneck

ناوردا: cap[u][v] همیشه ظرفیت باقی‌مانده است: ظرفیت یال منهای جریانش، به‌اضافه‌ی جریانی که می‌شود از یال برعکس پس گرفت.

شروع اجرا

16131241492074SABCDT
  • گره‌ی جاری (u)
  • درخت پیمایش (از parent)
  • یالِ در حال بررسی

صف queue

خالی

متغیرهای محلی

s'S'

نارنجی = در همین گام عوض شد

graph dict
  • 'S': [('A', 16), ('B', 13)]
  • 'A': [('C', 12)]
  • 'B': [('A', 4), ('D', 14)]
  • 'C': [('B', 9), ('T', 20)]
  • 'D': [('C', 7), ('T', 4)]
  • 'T': []

Call stack

  1. edmonds_karp()خط 6
گام ۱ از ۴۱۳
خط سیاه = search · خط نارنجی = گام کلیدیsearch: 0/4 · take: 0/17 · reach: 0/18 · bottleneck: 0/3 · push: 0/10 · augment: 0/3 · no_path: 0/1

    رشد تعداد عملیات با اندازه‌ی ورودی

    میانگین ۵ گراف تصادفی برای هر n (با seed ثابت). نشانگر توخالی = ورودی فعلی.

    • take
    • push
    • ورودی فعلی (n=6)
    01020304812n = تعداد گره‌هاtakepush
    نمایش به شکل جدول
    ntakepush
    47.85.2
    820.48.8
    1228.211.6
    توضیح

    ایده

    هر یال یک ظرفیت دارد. می‌خواهیم بیشترین جریان ممکن را از مبدأ (اینجا گره‌ی انتخاب‌شده) به مقصد (آخرین گره) بفرستیم، بی‌آنکه از ظرفیت هیچ یالی بیشتر شود و هر چه به گره‌ای وارد می‌شود از آن خارج شود.

    روش فورد–فالکرسون

    1. در شبکه‌ی باقی‌مانده (ظرفیت‌هایی که هنوز آزادند) یک مسیر از مبدأ به مقصد پیدا کن: مسیر افزایشی.
    2. کمترین ظرفیت روی مسیر (گلوگاه) را بفرست.
    3. برای هر یال u → v که جریان گرفت، به یال برعکس v → u همان‌قدر ظرفیت بده تا بعداً بشود این جریان را پس گرفت.

    تا وقتی مسیر افزایشی هست ادامه بده. قضیه‌ی جریان بیشینه – برش کمینه می‌گوید جوابِ پایانی بهینه است.

    چرا BFS؟

    ادموندز و کارپ نشان دادند اگر همیشه کوتاه‌ترین مسیر (با BFS) را برداریم، تعداد دورها حداکثر O(V·E) است: جمعاً O(V·E²)، مستقل از اندازه‌ی ظرفیت‌ها.

    در trace به چه چیزی نگاه کنی

    • یال‌های رنگی درخت BFS فعلی‌اند و مسیر افزایشی روی آن است.
    • دیکشنری cap در پنل حافظه ظرفیت‌های باقی‌مانده است؛ ورودی «پس گرفتن جریان» استفاده از یال برعکس را نشان می‌دهد.