#include <bits/stdc++.h>

#ifdef DEBUG
#define PRINT(x)\
    cout<<"func "<<__func__<<": line "<<__LINE__<<": "<<#x<<" = "<<(x)<<endl;
#define PRINTA(a,first,last)\
    cout<<"func "<<__func__<<": line "<<__LINE__<<": "<<#a<<"["<<(first)<<", "<<(last)<<")"<<endl;\
    for (int i=(first);i<(last);++i){cout<<#a<<"["<<i<<"] = "<<(a)[i]<<endl;}
#else
#define PRINT(x)
#define PRINTA(a,first,last)
#endif

#define FOR(i,a,b) for (int i=(a);i<(b);i++)
#define RFOR(i,a,b) for (int i=(b)-1;i>=(a);i--)
#define REP(i,n) for (int i=0;i<(n);i++)
#define RREP(i,n) for (int i=(n)-1;i>=0;i--)
#define pb push_back
#define mp make_pair
#define all(a) (a).begin(),(a).end()
#define MOD 1000000007

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template <class T, class U> void amax(T& x, U y) {if (x < y) x = y;}
template <class T, class U> void amin(T& x, U y) {if (x > y) x = y;}


// 逆辺の情報付き
template <class T>
struct edge
{
    int to;
    T cap;
    int rev;
    edge(int to, T cap, int rev) : to(to), cap(cap), rev(rev) {}
};

// 逆辺の情報付き有向グラフ
template <class T>
class graph
{
    private:
        const int number_of_nodes;
        const bool is_directed;
    public:
        vector<edge<T> > *edges;
        graph(int number_of_nodes, bool is_directed = true) : number_of_nodes(number_of_nodes), is_directed(is_directed) {
            edges = new vector<edge<T> >[number_of_nodes];
        }
        ~graph() {
            delete [] edges;
        }
        int size() const {return number_of_nodes;}
        void make_edge(int u, int v, T cap) {
            edges[u].push_back(edge<T>(v, cap, edges[v].size()));
            edges[v].push_back(edge<T>(u, 0, edges[u].size() - 1));
        }
};

template <class T>
T maximum_flow(graph<T> &g, int source, int sink)
{
    bool *used = new bool[g.size()]();
    function<T(int, T)> dfs = [&](int u, T f) -> T {
        //printf("u = %d, f = %d in dfs(u,f)\n", u, f);
        if (u == sink) {
            return f;
        }
        used[u] = true;
        for (edge<T> &e: g.edges[u]) {
            if (!used[e.to] && e.cap > 0) {
                /*if (u == 9) {
                    printf("(u, e.to), cost = e.cap\n", u, e.to, cost);
                }*/
                //T d = dfs(e.to, f == -1 ? e.cap : min(f, e.cap));
                //T d = dfs(e.to, u == source ? e.cap : min(f, e.cap));
                T d = dfs(e.to, min(f, e.cap));
                if (d > 0) {
                    //printf("take edge (%d, %d), flow %d\n", u, e.to, d);
                    e.cap -= d;
                    g.edges[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        //used[u] = false;
        //printf("back\n");
        return 0;
    };
    T result = 0;
    while (true) {
        //cout << "while" << endl;
        //memset(used, 0, sizeof(used));
        /*
        REP(i, g.size()) {
            if (used[i] != 0) {
                printf("error used[%d] = %d\n", i, used[i]);
            }
        }*/
        fill (used, used + g.size(), 0);
        //T flow = dfs(source, -1);
        T flow = dfs(source, 100000);
        PRINT(flow);
        /*
        REP(v, g.size()) {
            for (edge<T> &e: g.edges[v]) {
                printf("(%d, %d), cap = %d\n", v, e.to, e.cap);
            }
        }*/
        if (flow == 0) {
            return result;
        }
        result += flow;
    }
    delete [] used;
    return result;
}

int main(void)
{
    int V, E;
    cin >> V >> E;
    graph<int> g(V);
    REP(i, E) {
        int u, v, c;
        cin >> u >> v >> c;
        g.make_edge(u, v, c);
    }
    //cout << "input completed" << endl;
    cout << maximum_flow(g, 0, V - 1) << endl;
    return 0;
}




Measured memory_bytes: 3148