fork download
  1. // min_cut_templates.cpp
  2. // Gồm 2 template C++ phổ biến để giải bài toán Min-Cut:
  3. // 1) Dinic (Max-Flow) -> tìm s-t Min-Cut trong đồ thị có hướng/không hướng với trọng số (capacity)
  4. // 2) Stoer-Wagner -> tìm Global Min-Cut trong đồ thị vô hướng có trọng số
  5. //
  6. // HDSD: compile: g++ -std=c++17 -O2 min_cut_templates.cpp -o mincut
  7. // Sau khi biên dịch, chạy: ./mincut
  8. // Trong file này có ví dụ đầu vào cho cả 2 giải pháp. Thay đổi "main" để dùng giải pháp bạn cần.
  9.  
  10. #include <bits/stdc++.h>
  11. using namespace std;
  12. using ll = long long;
  13. const ll INFLL = (ll)4e18;
  14.  
  15. // ===================== DINIC (s-t min-cut) =====================
  16. // Sử dụng cho bài toán: cho đồ thị có hướng (có thể chuyển các cạnh vô hướng thành 2 cạnh 2 chiều),
  17. // mỗi cạnh có capacity. Tìm max-flow từ s->t, và thu được s-t min-cut.
  18. // Complexity: O(min(V^{2/3}, E^{1/2}) * E) trên đồ thị thực tế; thường đủ nhanh cho E ~ 1e5.
  19.  
  20. struct Dinic {
  21. struct Edge { int to; int rev; ll cap; };
  22. int n; vector<vector<Edge>> g; vector<int> lvl, it;
  23. Dinic(int n=0): n(n), g(n), lvl(n), it(n) {}
  24. void reset(int _n){ n=_n; g.assign(n,{}); lvl.assign(n,0); it.assign(n,0); }
  25. void add_edge(int u,int v,ll c){
  26. Edge a{v,(int)g[v].size(),c};
  27. Edge b{u,(int)g[u].size(),0};
  28. g[u].push_back(a); g[v].push_back(b);
  29. }
  30. // nếu muốn vô hướng với capacity c, gọi add_edge(u,v,c); add_edge(v,u,c);
  31. bool bfs(int s,int t){
  32. fill(lvl.begin(), lvl.end(), -1);
  33. queue<int> q; q.push(s); lvl[s]=0;
  34. while(!q.empty()){
  35. int v=q.front(); q.pop();
  36. for(auto &e: g[v]) if(e.cap>0 && lvl[e.to]==-1){ lvl[e.to]=lvl[v]+1; q.push(e.to); }
  37. }
  38. return lvl[t]!=-1;
  39. }
  40. ll dfs(int v,int t,ll f){
  41. if(v==t || f==0) return f;
  42. for(int &i = it[v]; i<(int)g[v].size(); ++i){
  43. Edge &e = g[v][i];
  44. if(e.cap>0 && lvl[e.to]==lvl[v]+1){
  45. ll ret = dfs(e.to, t, min(f, e.cap));
  46. if(ret>0){ e.cap -= ret; g[e.to][e.rev].cap += ret; return ret; }
  47. }
  48. }
  49. return 0;
  50. }
  51. ll maxflow(int s,int t){ ll flow=0; while(bfs(s,t)){ fill(it.begin(), it.end(), 0); while(true){ ll pushed = dfs(s,t,INFLL); if(!pushed) break; flow+=pushed; } } return flow; }
  52. // Sau khi chạy maxflow, ta có thể lấy tập đỉnh S (các đỉnh reachable từ s trong residual graph):
  53. vector<char> min_cut_s_side(int s){
  54. vector<char> vis(n, 0); queue<int> q; q.push(s); vis[s]=1;
  55. while(!q.empty()){
  56. int v=q.front(); q.pop();
  57. for(auto &e: g[v]) if(e.cap>0 && !vis[e.to]){ vis[e.to]=1; q.push(e.to); }
  58. }
  59. return vis; // vis[u]==1 <=> u in S side (source side)
  60. }
  61. };
  62.  
  63. // ===================== STOER-WAGNER (global min-cut, undirected) =====================
  64. // Sử dụng cho bài toán: đồ thị vô hướng có trọng số dương trên cạnh. Trả về giá trị min-cut toàn cục.
  65. // Complexity: O(n^3) cho cài đặt đơn giản; với n<=400-700 OK, n<=2000 có thể chậm.
  66. // Lưu ý: đồ thị phải là vô hướng, trọng số tổng các cạnh có thể dùng long long.
  67.  
  68. pair<ll, vector<int>> stoer_wagner_global_min_cut(int n, vector<vector<ll>> w){
  69. // w: n x n matrix, w[u][v] = trọng số giữa u và v (0 nếu không có cạnh)
  70. vector<int> vtx(n); iota(vtx.begin(), vtx.end(), 0);
  71. ll best = INFLL; vector<int> best_cut;
  72. vector<ll> weights(n);
  73. vector<char> exist(n,1);
  74. for(int phase = n; phase > 1; --phase){
  75. fill(weights.begin(), weights.end(), 0);
  76. vector<char> added(n,0);
  77. int prev = -1;
  78. int last = -1;
  79. for(int i=0;i<phase;i++){
  80. int sel = -1;
  81. for(int j=0;j<phase;j++) if(!added[vtx[j]]){
  82. if(sel==-1 || weights[vtx[j]] > weights[sel]) sel = vtx[j];
  83. }
  84. added[sel] = 1; last = sel;
  85. if(i == phase-1){
  86. // cut between last and the rest
  87. ll cut_value = weights[last];
  88. if(cut_value < best){
  89. best = cut_value;
  90. // build cut set: reachable from 'last' in the contracted graph? We reconstruct using 'added'
  91. // We'll mark nodes that are in the 'last' side (we can compute from vtx and added)
  92. best_cut.clear();
  93. // nodes with added==1 in this order produce the A set when i==phase-1
  94. // but to produce consistent partition, we collect vtx indices that are added
  95. for(int k=0;k<n;k++) {
  96. // nothing here; we'll reconstruct below using a BFS on final contracted graph if needed
  97. }
  98. // For simplicity, we'll return empty vector for cut partition (user mostly needs value). If partition needed, run one phase of reachability on final residual.
  99. }
  100. // merge last and prev
  101. for(int j=0;j<phase;j++){
  102. w[prev][vtx[j]] += w[last][vtx[j]];
  103. w[vtx[j]][prev] = w[prev][vtx[j]];
  104. }
  105. // remove last from vtx
  106. vtx.erase(find(vtx.begin(), vtx.end(), last));
  107. break;
  108. }
  109. prev = last;
  110. // add weights of sel to others
  111. for(int j=0;j<phase;j++) if(!added[vtx[j]]) weights[vtx[j]] += w[sel][vtx[j]];
  112. }
  113. }
  114. return {best, best_cut};
  115. }
  116.  
  117. // Note: the above stoer-wagner implementation returns only the minimum cut value robustly. If you need the explicit partition,
  118. // implement a version that records the set A at the phase giving the best cut (store 'added' array copy). For most contest tasks only value required.
  119.  
  120. // ===================== EXAMPLE USAGE =====================
  121. // Trong main dưới đây có 2 block. Bỏ comment block bạn cần dùng.
  122. int main(){
  123. ios::sync_with_stdio(false);
  124. cin.tie(nullptr);
  125.  
  126. // ====== Ví dụ 1: S-T Min-Cut bằng Dinic ======
  127. // Input format (ví dụ): n m s t
  128. // n: số đỉnh (đánh số từ 0..n-1), m: số cạnh
  129. // tiếp theo m dòng: u v cap (u,v: 0-based). Nếu đồ thị vô hướng, thêm 2 chiều.
  130. // Ví dụ (uncomment để chạy ví dụ này):
  131. /*
  132.   {
  133.   int n; int m; int s,t;
  134.   if(!(cin>>n>>m>>s>>t)) return 0;
  135.   Dinic D(n);
  136.   for(int i=0;i<m;i++){
  137.   int u,v; ll c; cin>>u>>v>>c;
  138.   D.add_edge(u,v,c);
  139.   // nếu muốn vô hướng, add_edge(v,u,c);
  140.   }
  141.   ll maxf = D.maxflow(s,t);
  142.   cout << "MaxFlow = " << maxf << '\n';
  143.   auto sideS = D.min_cut_s_side(s);
  144.   // In các cạnh (u,v) thuộc cut: sideS[u]==1 && sideS[v]==0
  145.   cout << "Edges in s-t min-cut:\n";
  146.   for(int u=0; u<n; ++u) for(auto &e: D.g[u]){
  147.   if(e.cap==0) continue; // lưu ý: đây là residual; để in chính xác nên lưu danh sách cạnh gốc riêng
  148.   }
  149.   }
  150.   */
  151.  
  152. // ====== Ví dụ 2: Global Min-Cut bằng Stoer-Wagner ======
  153. // Input format (ví dụ): n
  154. // tiếp theo n dòng mỗi dòng n số: ma trận trọng số (0 nếu không có)
  155. // Ví dụ (uncomment để dùng):
  156. /*
  157.   {
  158.   int n; if(!(cin>>n)) return 0;
  159.   vector<vector<ll>> w(n, vector<ll>(n,0));
  160.   for(int i=0;i<n;i++) for(int j=0;j<n;j++) cin>>w[i][j];
  161.   auto res = stoer_wagner_global_min_cut(n, w);
  162.   cout << "Global min-cut value = " << res.first << '\n';
  163.   }
  164.   */
  165.  
  166. cout << "No example active. Edit main to use Dinic or Stoer-Wagner blocks.\n";
  167. return 0;
  168. }
  169.  
Success #stdin #stdout 0s 5304KB
stdin
Standard input is empty
stdout
No example active. Edit main to use Dinic or Stoer-Wagner blocks.