#include <bits/stdc++.h>
using namespace std;
#define REP(i, n) for(int i = 0;i < n;i++)
#define REPR(i, n) for(int i = n;i >= 0;i--)
#define FOR(i, m, n) for(int i = m;i < n;i++)
#define fi first
#define se second
#define mp make_pair
#define itrfor(itr,A) for(auto itr = A.begin(); itr !=A.end();itr++)
template <class T> using reversed_priority_queue = priority_queue<T, vector<T>, greater<T> >;
typedef long long llong;
char moji[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
char moji2[26]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
char moji3[10]={'0','1','2','3','4','5','6','7','8','9'};
#define Sort(a) sort(a.begin(),a.end());
#define Reverse(a) reverse(a.begin(),a.end());
#define print(a) cout << a << endl;
#define MOD llong(1e9+7)
#define MAX int(2 * 1e5 +5)
#define debug(x)  cout << #x << " = " << (x) << endl;
#define pi acos(-1.0)
#define int llong
#define INF llong(1e18 + 3)
template<class T> bool chmax(T &a,  T b) {if(a<b){a=b; return 1;} return 0;}
template<class T> bool chmin(T &a,  T b) {if(a>b){a=b; return 1;} return 0;}
bool Add(int &a,int b){a = (a + b) % MOD;}
void myprint(int* A,int A_num){
   REP(i,A_num) cout << A[i] << " ";
   cout << endl;
}


/*
FIFO式のpreflow_push.
計算量O(n^3)
*/




//辺を表す構造体．逆向きの辺はedges[to][rev]
template < typename T> struct edge{
    int to,rev;
    T cap;//辺の容量

};

template <typename T> class Max_Flow{
    int size;
    vector<edge<T>>* edges;
    int *d;//距離ラベル
    int *e;//各頂点のexcessを管理する
    int *current_edge;//各頂点のcurrent edgeを管理

public:
    Max_Flow(int n){
        size = n;
        edges = new vector<edge<T>>[size];
        d = new int[size];
        e = new int[size];
        current_edge = new int[size];
    };

    void add_edge(int from,int to,T cap);//from からtoへ容量capの辺を追加

    void Disp_Graph();//グラフを表示

    void disp_d(); // dを表示

    void relabel(int v);

    T Get_Max_Flow(int s,int t);//現在所持しているグラフの最大流量を返す

    void flow_push(int from, int to);

};

//残余グラフ用に，逆向きの辺も追加．元のグラフに両方向の辺がある場合は，合計4本の枝を貼ることになる
template<typename T> void Max_Flow<T>:: add_edge(int from,int to,T cap){
    edges[from].push_back( edge<T> {to, int(edges[to].size()) , cap });
    edges[to].push_back(edge<T> {from,int( edges[from].size() - 1) , T(0) });
}

//各頂点ごとに枝を表示する
template<typename T> void Max_Flow<T>:: Disp_Graph(){
    REP(i,size){
        itrfor(itr,edges[i]){
            cout << i << " " << itr->to << " " << itr->cap << endl;
        }
        cout << endl;
    }
}

template<typename T> void  Max_Flow<T>:: relabel(int v ){
    int label = INF;
    itrfor(itr,edges[v]){
        if(itr->cap > 0 ) chmin(label,d[itr->to] + 1);
    }
    d[v] = label;
}




//現在のグラフからMax_Flowを返す
template<typename T> T Max_Flow<T>:: Get_Max_Flow(int s,int t){
    //初期化
    REP(i,size) d[i] = 0;
    d[s] = size;
    REP(i,size) e[i] = 0;
    REP(i,size) current_edge[i] = 0;

    queue<int> Q; // activeな頂点を保持する

    // sから出る辺にcapacity目一杯フローを流す
    itrfor(itr,edges[s]){
        if(itr->to == t){// sからtへ直接つながる枝があったら，フロー流す
            e[t] += itr->cap;
            itr->cap = 0;
            continue;
        }
        e[itr->to] += itr->cap;
        edges[itr->to][itr->rev].cap += itr->cap;
        itr->cap = 0;
        Q.push(itr->to);
    }


    while(Q.size()){
        int v = Q.front();
        Q.pop();
        
        // e(v) = 0 となるか，relabelするまで，一つの頂点にpush/relabel操作を続ける
        while(1){
            // edgeを一周したらrelabelする
            if(current_edge[v] == edges[v].size() and e[v] > 0){
                relabel(v);
                current_edge[v] = 0;
                Q.push(v);
                break;
            }
            if(e[v] == 0 ) break;
            int w = edges[v][current_edge[v]].to;
            if( edges[v][current_edge[v]].cap > 0 and e[v] > 0 and d[v] == d[w]  +  1){
                T amount = min(edges[v][current_edge[v]].cap,e[v]);
                if(e[w] == 0 and w != t and w != s) Q.push(w);
                e[w] += amount;
                e[v] -= amount;
                edges[w][edges[v][current_edge[v]].rev].cap += amount;
                edges[v][current_edge[v]].cap -= amount;
            }
            if(e[v] == 0) break;
            current_edge[v]++;
        }
    }

    return e[t];



}


template<typename T> void Max_Flow<T>:: disp_d(){
    REP(i,size) cout << i << " " << d[i] << endl;
}







signed main(){
    int n,m;
    cin >> n >> m;
    Max_Flow<int> M(n);
    REP(i,m){
        int from,to,cap;
        cin >> from >> to >> cap;
        M.add_edge(from,to,cap);
    }


    cout << M.Get_Max_Flow(0,n - 1) << endl;



}


Measured memory_bytes: 3216