// Edmonds - Karp
// O (N * M * M)
#include <fstream>
#include <algorithm>
using namespace std;
const int kMaxN = 1000;
const int kMaxM = 5000;
const int kInf = 2000000000;
const int NIL = -1;
struct Edge {
int v, next;
int cap;
};
Edge l[2 * kMaxM]; // buffer de liste alocate static, pozitiile impare corespund muchiilor din reteaua reziduala
int adj[kMaxN]; // capete de liste de adiacenta
int Q[kMaxN]; // coada BFS
int qStart, qEnd;
int father[kMaxN]; // vector de tati, pentru drumul de ameliorare
int edgePointer[kMaxN]; // pointer catre muchia orientata tata -> fiu
int N;
void addEdge(const int &u, const int &v, const int &cap, const int &pos) {
l[pos].v = v;
l[pos].cap = cap;
l[pos].next = adj[u];
adj[u] = pos;
}
int BFS(const int &source, const int &sync) {
int u, v;
for (int i = 0; i < N; i++) {
father[i] = NIL;
}
// cauta drumul de ameliorare
father[source] = source;
qStart = 0;
qEnd = 1;
Q[0] = source;
do {
u = Q[qStart++];
for (int i = adj[u]; i != NIL; i = l[i].next) {
v = l[i].v;
if ((father[v] == NIL) && (l[i].cap != 0)) {
father[v] = u;
edgePointer[v] = i;
Q[qEnd++] = v;
}
}
} while ((qStart != qEnd) && (father[sync] == NIL));
int flow = 0;
// pornind de la destinatie
for (int i = adj[sync]; i != NIL; i = l[i].next) {
v = l[i].v;
if ((father[v] != NIL) && (l[i ^ 1].cap)) { // muchia e orientata
father[sync] = v; // v -> destinatie = i ^ 1
edgePointer[sync] = i ^ 1;
// vor fi saturate toate muchiile de capacitate minim
int minFlow = kInf;
u = sync;
do {
minFlow = min(minFlow, l[edgePointer[u]].cap);
u = father[u];
} while (u != source);
u = sync;
do {
l[edgePointer[u]].cap -= minFlow; // scade din capacitatea muchiei
l[edgePointer[u] ^ 1].cap += minFlow; // adauga in reteaua reziduala
u = father[u];
} while (u != source);
flow += minFlow;
}
}
return flow;
}
int main(void) {
ifstream in("maxflow.in");
ofstream out("maxflow.out");
int M;
in >> N >> M;
for (int i = 0; i < N; i++) {
adj[i] = NIL;
}
for (int i = 0; i < M; i++) {
int u, v, cap;
in >> u >> v >> cap;
addEdge(u - 1, v - 1, cap, 2 * i); // retea de transport
addEdge(v - 1, u - 1, 0, 2 * i + 1); // retea reziduala
}
in.close();
int flow = 0, augment;
do {
augment = BFS(0, N - 1);
flow += augment;
} while (augment);
out << flow << '\n';
out.close();
return 0;
}