#pragma region Macros
#include <bits/stdc++.h>
using namespace std;
// iteration helper
#define FOR(i,l,r) for(int i=(l);i<(r);++i)
#define REP(i,n)  FOR(i,0,n)
#define RFOR(i,l,r) for(int i=(r)-1;i>=(l);--i)
#define RREP(i,n) RFOR(i,0,n)
#define EACH(i,c) for(typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)
#define ALL(a) (a).begin(), (a).end()
#define RALL(a) (a).rbegin(), (a).rend()
#define EXIST(s,e) ((s).find(e)!=(s).end())
#define PB push_back
// typedefs
#define ll long long
#define vi vector<int>
#define vi2 vector<vi>
// input
#define ci(x) int x;cin>>x
#define cs(x) string x;cin>>x
#define cd(x) double x;cin>>x
#define cvi(x,n) vi x(n);REP(i,n){cin>>x[i];}
// const input
#define cci(x) const int x = [](){int t;cin>>t;return t;}()
#define ccs(x) const string x = [](){string t;cin>>t;return t;}()
#define ccd(x) const double x = [](){double t;cin>>t;return t;}()
// output
#define pr(x) cout<<x<<endl
#define prvec(v) REP(i,v.size()) pr(v[i])
#define ppr(x) cout<<x
#define lf() cout<<endl
// debug methods
// usage: debug(x,y);
#define CHOOSE(a) CHOOSE2 a
#define CHOOSE2(a0,a1,a2,a3,a4,x,...) x
#define debug_1(x1) cout<<#x1<<": "<<x1<<endl
#define debug_2(x1,x2) cout<<#x1<<": "<<x1<<", "#x2<<": "<<x2<<endl
#define debug_3(x1,x2,x3) cout<<#x1<<": "<<x1<<", "#x2<<": "<<x2<<", "#x3<<": "<<x3<<endl
#define debug_4(x1,x2,x3,x4) cout<<#x1<<": "<<x1<<", "#x2<<": "<<x2<<", "#x3<<": "<<x3<<", "#x4<<": "<<x4<<endl
#define debug_5(x1,x2,x3,x4,x5) cout<<#x1<<": "<<x1<<", "#x2<<": "<<x2<<", "#x3<<": "<<x3<<", "#x4<<": "<<x4<<", "#x5<<": "<<x5<<endl
#ifdef _DEBUG
#define debug(...) CHOOSE((__VA_ARGS__,debug_5,debug_4,debug_3,debug_2,debug_1,~))(__VA_ARGS__)
#else
#define debug(...)
#endif
// const number
const double PI  = acos(-1.0);
const double EPS = 1e-10;

int powint(int a, int x){
	int res = 1;
	REP(i,x){
		res = res *a; 
	}
	return res;
}
#pragma endregion

// Ford-Fulkerson

struct edge {
	int to;//行き先
	int cap;//容量
	int rev;//逆向きの辺が行き先の頂点の何番目の辺か
};

//グラフの隣接リスト表現
vector<vector<edge>> G;

//DFSで既に調べた頂点か
vector<bool> used;

//パスを変更しつつやるDFS
// v:始点, t:終点, f:流したい量
// 返値: 流せるフロー
int dfs(int v, int t, int f){
	if (v == t){
		// 始点と終点が同じ時は流したい量がそのまま流れる
		return f;
	}
	used[v] = true;
	//この点から出る各辺について
	REP(i, G[v].size()){
		edge& e = G[v][i];//辺
		//深さ優先探索
		if(!used[e.to] && e.cap>0){
			// この先で流せるフロー
			int saki = dfs(e.to, t, min(f, e.cap));
			if(saki>0){
				//残余グラフを生成（G自体を書き換える）
				e.cap -= saki;
				G[e.to][e.rev].cap += saki;
				//残余グラフを生成した場合は即戻る
				return saki;
			}
		}
	}
	//もう流せない場合
	return 0;
}

void print_graph(){
	REP(i,G.size()){
		REP(j, G[i].size()){
			cout<<i<<" to "<<G[i][j].to<<" (cap: "<<G[i][j].cap<<") "<<endl;
		}
	}
}

signed main(){
#ifdef _DEBUG
try{
#endif
// main program ---------------------------------------------
	cci(nv); cci(ne);
	G = vector<vector<edge>>(nv);
	used = vector<bool>(nv);

	//辺の追加
	REP(i, ne){
		int u, v, c;
		cin>>u>>v>>c;
		G[u].PB({ v, c, (int)G[v].size()});
		G[v].PB({ u, 0, (int)G[u].size()-1});
	}

	//探索
	int ans = 0;
	int s = 0;//始点
	int t = nv-1;//終点
	while(true){
		used = vector<bool>(nv, false);//usedをdfsの前に初期化
		//print_graph();
		int f = dfs(s, t, INT_MAX);
		if(f==0){
			//グラフをこれ以上書き換えられなくなったら終わる
			break;
		}
		ans += f;
	}
	pr(ans);

// end main program -----------------------------------------
#ifdef _DEBUG
} catch (std::out_of_range& ex){ pr("out of range"); }
#endif
	return 0;
}



Measured memory_bytes: 3200