#include <iostream>
#include <vector>
using namespace std;
/* Problem statement: You are given a network in which edges are
* associated capacities. You are to compute the maximum flow through the network
* such that the following conditions are satisfied:
*
* 1. 0 <= flow(edge) <= capacity(edge)
* 2. flow_in(node) = flow_out(node)
*
* Residual capacity: a mapping r : E -> R+, r(e) = c(e) - f(e)
*
* Theorem
* ========
* A flow network N with a flow f is a maximum flow iff there is no
* augmenting path in the network.
*
* Augmenting path
* ===============
* Path in the network from the source to the sink
* such that the residual capacity of every contaed edge is nonzero.
*
* Ford-Fulkerson description
* ==========================
*
* Pseudo-code1
* ============
* initialize flow to zero
* path = AugmentingPath(G, s, t)
* while (there is an augmenting path)
* augment flow along path
* update residual capacities
* path = AugmentingPath(G, s, t)
*
*
*Pseudo-code2
*============
* for each edge in the graph
* initialize flow to zero
*
* while (there is a path p from s to t in the resigual graph)
* augmenting_flow = min(residual_capacity((u,v)) for each edge (u,v) in the path)
* flow += augmenting_flow
* // update the residual graph
* for each edge (u,v) in p:
* if (u,v) is a forward edge:
* flow(u,v) += augmenting_flow;
* if (u,v) is backward edge:
* flow(u,v) -= augmenting_flow;
* return flow
*
*/
const int NMAX = 1e3;
struct Graph{
int num_vertices;
typedef long long flow_type;
struct Edge{
int dst;
flow_type capacity, flow;
size_t rev;
};
vector<vector<Edge>> e;
Graph(int n) : num_vertices(n), e(n) {}
void add_edge(int src, int dst, int capacity){
e[src].push_back({dst, capacity, 0, e[dst].size()});
e[dst].push_back({src, 0, 0, e[src].size()});
}
flow_type max_flow(int source, int sink){
bitset<NMAX+1> vis;
function <flow_type(int, flow_type)> aug_path = [&](int node, flow_type path_flow) {
// check termination case when the current node is sink
if(node == sink)
return path_flow;
vis.set(node);
for(auto &edge : e[node]) {
int son = edge.dst;
// positive residual capacity -> can augment
if (!vis.test(son) && edge.capacity > edge.flow) {
flow_type res_capacity = edge.capacity - edge.flow;
flow_type f = aug_path(son, min(path_flow, res_capacity));
// update the info for residual graph
if (f > 0) {
edge.flow += f;
e[edge.dst][edge.rev].flow -= f;
return f;
}
}
}
return flow_type(0);
};
// initialize flow on each edge as zero in the beginning
for (int i = 0; i < num_vertices; ++i)
for (auto &edge : e[i])
edge.flow = 0;
flow_type flow = 0;
while(true) {
vis.reset(); // reset the visited nodes for the dfs
flow_type aug_flow = aug_path(source, sink);
if (aug_flow == 0) // test for termination case
break;
flow += aug_flow;
}
return flow;
}
};
int main() {
int n, m;
cin >> n >> m;
Graph g(n);
for (int i = 0; i < m; ++i) {
int x, y, c;
cin >> x >> y >> c;
x--, y--;
g.add_edge(x, y, c);
}
cout << g.max_flow(0, n-1);
return 0;
}