Skip to content

Commit

Permalink
Use binary indexed heap for min cost flow
Browse files Browse the repository at this point in the history
  • Loading branch information
indy256 committed Aug 25, 2022
1 parent 5463e2e commit 4af80c8
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 55 deletions.
35 changes: 21 additions & 14 deletions cpp/graphs/flows/min_cost_flow_dijkstra.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <bits/stdc++.h>

#include "../../structures/binary_heap_indexed.h"

using namespace std;

// https://cp-algorithms.com/graph/min_cost_flow.html in O(E * V + E * logV * FLOW)
Expand Down Expand Up @@ -49,27 +51,30 @@ struct min_cost_flow {
}
}

void dijkstra(int s, vector<int> &pot, vector<int> &dist, vector<int> &curflow, vector<int> &prevnode,
vector<int> &prevedge) {
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
q.emplace(0, s);
void dijkstra(int s, int t, vector<int> &pot, vector<int> &dist, vector<bool> &finished, vector<int> &curflow,
vector<int> &prevnode, vector<int> &prevedge) {
binary_heap_indexed<int> h(graph.size());
h.add(s, 0);
fill(dist.begin(), dist.end(), numeric_limits<int>::max());
dist[s] = 0;
curflow[s] = numeric_limits<int>::max();
while (!q.empty()) {
auto [d, u] = q.top();
q.pop();
if (d != dist[u])
continue;
fill(finished.begin(), finished.end(), false);
while (!finished[t] && h.size != 0) {
int u = h.remove_min();
finished[u] = true;
for (size_t i = 0; i < graph[u].size(); i++) {
Edge &e = graph[u][i];
int v = e.to;
if (e.cap <= e.f)
if (e.f >= e.cap)
continue;
int v = e.to;
int nprio = dist[u] + e.cost + pot[u] - pot[v];

if (dist[v] > nprio) {
if (dist[v] == numeric_limits<int>::max())
h.add(v, nprio);
else
h.changePriority(v, nprio);
dist[v] = nprio;
q.emplace(nprio, v);
prevnode[v] = u;
prevedge[v] = i;
curflow[v] = min(curflow[u], e.cap - e.f);
Expand All @@ -81,15 +86,17 @@ struct min_cost_flow {
tuple<int, int> cal_min_cost_flow(int s, int t, int maxf) {
size_t n = graph.size();
vector<int> pot(n), curflow(n), dist(n), prevnode(n), prevedge(n);
vector<bool> finished(n);
bellman_ford(s, pot); // this can be commented out if edges costs are non-negative
int flow = 0;
int flow_cost = 0;
while (flow < maxf) {
dijkstra(s, pot, dist, curflow, prevnode, prevedge);
dijkstra(s, t, pot, dist, finished, curflow, prevnode, prevedge);
if (dist[t] == numeric_limits<int>::max())
break;
for (size_t i = 0; i < n; i++)
pot[i] += dist[i];
if (finished[i])
pot[i] += dist[i] - dist[t];
int df = min(curflow[t], maxf - flow);
flow += df;
for (int v = t; v != s; v = prevnode[v]) {
Expand Down
18 changes: 18 additions & 0 deletions cpp/structures/binary_heap_indexed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "binary_heap_indexed.h"

#include <bits/stdc++.h>

using namespace std;

// usage example
int main() {
binary_heap_indexed<int> h(5);
h.add(0, 50);
h.add(1, 30);
h.add(2, 40);
h.changePriority(0, 20);
h.remove(1);
while (h.size) {
cout << h.remove_min() << endl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
using namespace std;

template <class T>
struct binary_heap {
struct binary_heap_indexed {
vector<T> heap;
vector<int> pos2Id;
vector<int> id2Pos;
int size;

binary_heap() : size(0) {}
binary_heap_indexed() : size(0) {}

binary_heap(int n) : heap(n), pos2Id(n), id2Pos(n), size(0) {}
binary_heap_indexed(int n) : heap(n), pos2Id(n), id2Pos(n), size(0) {}

void add(int id, T value) {
heap[size] = value;
Expand Down Expand Up @@ -78,16 +78,3 @@ struct binary_heap {
id2Pos[pos2Id[j]] = j;
}
};

// usage example
int main() {
binary_heap<int> h(5);
h.add(0, 50);
h.add(1, 30);
h.add(2, 40);
h.changePriority(0, 20);
h.remove(1);
while (h.size) {
cout << h.remove_min() << endl;
}
}
28 changes: 14 additions & 14 deletions java/graphs/flows/MinCostFlowDijkstra.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.*;
import java.util.stream.Stream;
import structures.BinaryHeapIndexed;

// https://cp-algorithms.com/graph/min_cost_flow.html in O(E * V + min(E * logV * FLOW, V^2 * FLOW))
// negative-cost edges are allowed
Expand All @@ -13,7 +14,7 @@ public MinCostFlowDijkstra(int nodes) {
graph = Stream.generate(ArrayList::new).limit(nodes).toArray(List[] ::new);
}

class Edge {
static class Edge {
int to, rev, cap, f, cost;

Edge(int to, int rev, int cap, int cost) {
Expand Down Expand Up @@ -57,20 +58,16 @@ void bellmanFord(int s, int[] dist) {
}
}

void dijkstra(
void dijkstraSparse(
int s, int t, int[] pot, int[] dist, boolean[] finished, int[] curflow, int[] prevnode, int[] prevedge) {
PriorityQueue<Long> q = new PriorityQueue<>();
q.add((long) s);
BinaryHeapIndexed h = new BinaryHeapIndexed(graph.length);
h.add(s, 0);
Arrays.fill(dist, Integer.MAX_VALUE);
dist[s] = 0;
Arrays.fill(finished, false);
curflow[s] = Integer.MAX_VALUE;
while (!finished[t] && !q.isEmpty()) {
long cur = q.remove();
int u = (int) (cur & 0xFFFF_FFFFL);
int priou = (int) (cur >>> 32);
if (priou != dist[u])
continue;
while (!finished[t] && h.size != 0) {
int u = h.removeMin();
finished[u] = true;
for (int i = 0; i < graph[u].size(); i++) {
Edge e = graph[u].get(i);
Expand All @@ -79,8 +76,11 @@ void dijkstra(
int v = e.to;
int nprio = dist[u] + e.cost + pot[u] - pot[v];
if (dist[v] > nprio) {
if (dist[v] == Integer.MAX_VALUE)
h.add(v, nprio);
else
h.changePriority(v, nprio);
dist[v] = nprio;
q.add(((long) nprio << 32) + v);
prevnode[v] = u;
prevedge[v] = i;
curflow[v] = Math.min(curflow[u], e.cap - e.f);
Expand All @@ -89,7 +89,7 @@ void dijkstra(
}
}

void dijkstra2(
void dijkstraDense(
int s, int t, int[] pot, int[] dist, boolean[] finished, int[] curflow, int[] prevnode, int[] prevedge) {
Arrays.fill(dist, Integer.MAX_VALUE);
dist[s] = 0;
Expand Down Expand Up @@ -133,8 +133,8 @@ public int[] minCostFlow(int s, int t, int maxf) {
int flow = 0;
int flowCost = 0;
while (flow < maxf) {
dijkstra(s, t, pot, dist, finished, curflow, prevnode, prevedge); // E*logV
// dijkstra2(s, t, pot, dist, finished, curflow, prevnode, prevedge); // V^2
dijkstraSparse(s, t, pot, dist, finished, curflow, prevnode, prevedge); // O(E*logV)
// dijkstraDense(s, t, pot, dist, finished, curflow, prevnode, prevedge); // O(V^2)
if (dist[t] == Integer.MAX_VALUE)
break;
for (int i = 0; i < n; i++)
Expand Down
22 changes: 11 additions & 11 deletions java/structures/BinaryHeapIndexed.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
// https://en.wikipedia.org/wiki/Binary_heap
// invariant: heap[parent] <= heap[child]
public class BinaryHeapIndexed {
long[] heap;
int[] heap;
int[] pos2Id;
int[] id2Pos;
int size;
public int size;

public BinaryHeapIndexed(int n) {
heap = new long[n];
heap = new int[n];
pos2Id = new int[n];
id2Pos = new int[n];
}

public void add(int id, long value) {
public void add(int id, int value) {
heap[size] = value;
pos2Id[size] = id;
id2Pos[id] = size;
Expand All @@ -30,14 +30,14 @@ public int removeMin() {
return removedId;
}

public void removeMin(int id) {
public void remove(int id) {
int pos = id2Pos[id];
pos2Id[pos] = pos2Id[--size];
id2Pos[pos2Id[pos]] = pos;
changePriority(pos2Id[pos], heap[size]);
}

public void changePriority(int id, long value) {
public void changePriority(int id, int value) {
int pos = id2Pos[id];
if (heap[pos] < value) {
heap[pos] = value;
Expand Down Expand Up @@ -73,12 +73,12 @@ void down(int pos) {
}

void swap(int i, int j) {
long tt = heap[i];
int t = heap[i];
heap[i] = heap[j];
heap[j] = tt;
int t = pos2Id[i];
heap[j] = t;
int tt = pos2Id[i];
pos2Id[i] = pos2Id[j];
pos2Id[j] = t;
pos2Id[j] = tt;
id2Pos[pos2Id[i]] = i;
id2Pos[pos2Id[j]] = j;
}
Expand All @@ -92,7 +92,7 @@ public static void main(String[] args) {

heap.changePriority(1, 3);
heap.changePriority(2, 6);
heap.removeMin(0);
heap.remove(0);

// print elements in sorted order
while (heap.size != 0) {
Expand Down

0 comments on commit 4af80c8

Please sign in to comment.