Cod sursa(job #2690681)

Utilizator nicu_ducalNicu Ducal nicu_ducal Data 25 decembrie 2020 10:06:08
Problema Flux maxim de cost minim Scor 70
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 5.38 kb
#include <bits/stdc++.h>

using namespace std;

template<typename T> ostream &operator<<(ostream &os, const vector<T> &v) { os << '{'; string sep; for (const auto &x : v) os << sep << x, sep = ", "; return os << '}'; }
template<typename A, typename B> ostream &operator<<(ostream &os, const pair<A, B> &p) { return os << '(' << p.first << ", " << p.second << ')'; }

using i64 = long long int;

const int INF = INT_MAX, MOD = 1e9 + 7;
const double EPS = 1e-9, PI = acos(-1);
const int dx[] = {0, 0, 0, -1, 1, -1, 1, 1, -1};
const int dy[] = {0, -1, 1, 0, 0, -1, 1, -1, 1};

/**
 *  Algorithm: Min-cost max-flow
 *  Complexity: Using only Bellman-Ford SPFA: O(|V|^2 * |E|^2)
 *              Using SPFA + Dijkstra with Johnson's Implementation: O(|V| * |E|^2 * log(|V|))
 */
struct Graph {
  vector<vector<int>> adj;
  vector<vector<int64_t>> capacity, cost;
  vector<bool> marked;
  vector<int> parent;
  vector<int64_t> bellman_distance/*, new_bellman_distance*/, distance;
  int n;
  int64_t max_flow, mincost_maxflow;

  Graph(int _n = 0) {
    init(_n);
  }

  void init(int _n) {
    n = _n;
    adj.resize(n + 1);
    cost.assign(n + 1, vector<int64_t>(n + 1, 0));
    capacity.assign(n + 1, vector<int64_t>(n + 1, 0));
    parent.assign(n + 1, -1);
    bellman_distance.assign(n + 1, INF);
    //new_bellman_distance.assign(n + 1, INF);
    distance.assign(n + 1, INF);
  }

  void addEdge(int u, int v, int cs, int cap) {
    adj[u].push_back(v);
    adj[v].push_back(u);
    cost[u][v] = cs;
    cost[v][u] = -cs;
    capacity[u][v] = cap;
  }

  void BellmanFordSPFA(int s, int d) {
    queue<int> q;
    vector<bool> in_queue(n + 1, false);
    q.push(s);
    in_queue[s] = true;
    bellman_distance[s] = 0;
    while (!q.empty()) {
      int cur = q.front();
      q.pop();
      in_queue[cur] = false;
      for (auto next: adj[cur]) {
        if (capacity[cur][next] && bellman_distance[next] > bellman_distance[cur] + cost[cur][next]) {
          bellman_distance[next] = bellman_distance[cur] + cost[cur][next];
          if (!in_queue[next]) {
            q.push(next);
            in_queue[next] = true;
          }
        }
      }
    }
  }

  /**
   * This is a modification of the original Dijkstra algorithm to eliminate the negative weights
   * The idea behind this implementation is that the distance to a node v from node u is changed to
   * mod_cost[u -> v] = cost[u -> u] + pi[v] - pi[u], where pi[x] represents the real distance from
   * the initial node to node x. In terms of minimal costs, pi can be found initially with Bellman-Ford
   * and then updated in the Dijsktra's body.
   * In my implementation:
   *    bellman_distance[x] = pi[x]
   *    new_bellman_distance[x] = The new pi for the graph after augmenting paths
   *    distance[x] = The distance to node x with mod_costs
   */
  bool JohnsonsDijkstra(int s, int d) {
    vector<int64_t> new_bellman_distance(n + 1, INF);
    priority_queue<pair<int64_t, int>, vector<pair<int64_t, int>>, greater<pair<int64_t, int>>> pq;
    pq.push({0, s});
    distance.assign(n + 1, INF);
    new_bellman_distance.assign(n + 1, INF);
    distance[s] = 0;
    new_bellman_distance[s] = 0;

    while (!pq.empty()) {
      auto current = pq.top();
      pq.pop();

      int cur = current.second;
      int64_t cst = current.first;
      if (distance[cur] != cst) continue;

      for (auto next: adj[cur]) {
        if (capacity[cur][next]) {
          if (distance[next] > distance[cur] + cost[cur][next] + bellman_distance[cur] - bellman_distance[next]) {
            distance[next] = distance[cur] + cost[cur][next] + bellman_distance[cur] - bellman_distance[next];
            parent[next] = cur;
            new_bellman_distance[next] = new_bellman_distance[cur] + cost[cur][next];
            pq.push({distance[next], next});
          }
        }
      }
    }

    /// This is the end case. If we haven't reached the sink, then there are no more augmenting paths
    if (distance[d] == INF) return false;

    /// Else, let's compute the flow for the best path (the cost of the path will be stored into
    /// as the value of new_bellman_distance[d]
    int64_t current_flow = INF, current_cost = new_bellman_distance[d];

    int path = d;
    while (path != s) {
      current_flow = min(current_flow, capacity[parent[path]][path]);
      path = parent[path];
    }

    max_flow += current_flow;
    mincost_maxflow += current_flow * current_cost;
    path = d;
    while (path != s) {
      capacity[parent[path]][path] -= current_flow;
      capacity[path][parent[path]] += current_flow;
      path = parent[path];
    }

    /// Update the bellman_distance
    bellman_distance = new_bellman_distance;

    return true;
  }

  void min_cost_max_flow(int s, int d) {
    max_flow = 0;
    mincost_maxflow = 0;
    BellmanFordSPFA(s, d);
    while (JohnsonsDijkstra(s, d));
  }
};

int main() {
  // ios_base::sync_with_stdio(false); cin.tie(nullptr);
  /// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

  ifstream cin("fmcm.in");
  ofstream cout("fmcm.out");

  int N, M, S, D, u, v, f, c;
  cin >> N >> M >> S >> D;
  Graph graph(N);
  for (int i = 1; i <= M; i++) {
    cin >> u >> v >> f >> c;
    graph.addEdge(u, v, c, f);
  }

  graph.min_cost_max_flow(S, D);
  cout << graph.mincost_maxflow << "\n";

  return 0;
}