#include <algorithm>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <limits>
#include <vector>
#include <queue>
#include <ctime>
#include <list>

using namespace std;


// Net class:

template <class Edge>
class Net {
	private:
		typedef Edge* EdgePointer;
		vector<EdgePointer> edges;
		vector<list<EdgePointer> > outerEdges;
		vector<list<EdgePointer> > innerEdges;

		// ХОЧЕТСЯ iter.begin(), iter.end(), iter ++, iter --, =, ==, (bool)
		class UniversalEdgeIterator {
			friend class Net;

			private:
				bool undefined = true;
				list<Edge*> *edgeList;
				typename list<Edge*>::iterator iter;
	
				void set (list<Edge*> *edgeList) {
					this->edgeList = edgeList;
				}

				void validate () {
					if (undefined)
						setBegin();

					undefined = false;
				}

			public:
				void setBegin () {
					iter = edgeList->begin();
				}
	
				bool isEnd () const {
					return iter == edgeList->end() || edgeList->size() == 0;
				}

				void move () {
					validate();
					iter = next(iter);
				}

				Edge* get () {
					validate();
					return *iter;
				}
		};

		vector<UniversalEdgeIterator> outerEdgeIterator;
		vector<UniversalEdgeIterator> innerEdgeIterator;

	public:
		typedef UniversalEdgeIterator EdgeIterator;
		typedef typename Edge::IndexType IndexType;
		IndexType source;
		IndexType sink;

		Net (const IndexType &verticesCount) {
			outerEdges.resize(verticesCount);
			innerEdges.resize(verticesCount);
			innerEdgeIterator.resize(verticesCount);
			outerEdgeIterator.resize(verticesCount);

			for (unsigned i = 0; i < verticesCount; i ++) {
				innerEdgeIterator[i].set(&innerEdges[i]);
				outerEdgeIterator[i].set(&outerEdges[i]);
			}
		}

		void addEdge (const Edge &edge) {
			EdgePointer edgePointer = new Edge(edge);
			edges.push_back(edgePointer);
			outerEdges[edgePointer->from].push_back(edges.back());
			innerEdges[edgePointer->to].push_back(edges.back());

			EdgePointer reverseEdgePointer = new Edge(edge.getReverse());
			edges.push_back(reverseEdgePointer);
			outerEdges[reverseEdgePointer->from].push_back(edges.back());
			innerEdges[reverseEdgePointer->to].push_back(edges.back());

			edgePointer->reverseEdge = reverseEdgePointer;
			reverseEdgePointer->reverseEdge = edgePointer;
		}

		EdgeIterator& getInnerEdgeIterator (const IndexType &vertex) {
			return innerEdgeIterator[vertex];
		}

		EdgeIterator getConstInnerEdgeIterator (const IndexType &vertex) const {
			return innerEdgeIterator[vertex];
		}

		EdgeIterator& getOuterEdgeIterator (const IndexType &vertex) {
			return outerEdgeIterator[vertex];
		}

		EdgeIterator getConstOuterEdgeIterator (const IndexType &vertex) const {
			return outerEdgeIterator[vertex];
		}

		IndexType getVerticesCount () const {
			return outerEdges.size();
		}
};

template <class Edge>
void printNet (Net<Edge> &net, ostream &os = cout) {
	for (typename Edge::IndexType vertex = 0; vertex < net.getVerticesCount(); vertex ++) {
		typename Net<Edge>::EdgeIterator iter = net.getConstOuterEdgeIterator(vertex);

		for (iter.setBegin(); !iter.isEnd(); iter.move()) {
			iter.get()->print(cout);
			cout << " ";
		}

		cout << endl;
	}
}


// Dinic algorithm:

template <class Edge>
typename Edge::CapacityType tryAugmentFlow (Net<Edge> &layeredNet, const typename Edge::IndexType &vertex, const typename Edge::CapacityType &augmentation = numeric_limits<typename Edge::CapacityType>::max()) {
	if (vertex == layeredNet.sink || augmentation == 0)
		return augmentation;
	
	for (typename Net<Edge>::EdgeIterator iter = layeredNet.getOuterEdgeIterator(vertex); !iter.isEnd(); iter.move()) {
		Edge *edge = iter.get();

		if (edge->capacity == 0)
			continue;

		typename Edge::CapacityType newExcess = min<typename Edge::CapacityType>(augmentation, edge->recidualCapacity());
		typename Edge::CapacityType augmentedFlow = tryAugmentFlow(layeredNet, edge->to, newExcess);

		if (augmentedFlow != 0) {
			edge->flow += augmentedFlow;
			edge->reverseEdge->flow -= augmentedFlow;
			return augmentedFlow;
		}
	}

	layeredNet.getOuterEdgeIterator(vertex).move();
	return 0;
}

template <class Edge>
typename Edge::CapacityType findBlockingFlow (Net<Edge> &layeredNet) {
	typename Edge::CapacityType blockingFlow = 0;

	while (true) {
		typename Edge::CapacityType augmentedFlow = tryAugmentFlow(layeredNet, layeredNet.source);
		if (augmentedFlow == 0)
			break;
		
		else
			blockingFlow += augmentedFlow;
	}

	return blockingFlow;
}


// Global blockingFlow -> maxFlow algorithm:

template <class Edge>
Net<Edge> buildLayeredNetwork (const Net<Edge> &net) {
	// BFS init:
	queue<typename Edge::IndexType> verticesQueue;
	vector<bool> used(net.getVerticesCount(), false);
	verticesQueue.push(net.source);

	// Layered Net init:
	Net<Edge> layeredNet(net.getVerticesCount());
	layeredNet.source = net.source;
	layeredNet.sink = net.sink;
	vector<typename Edge::IndexType> layers(net.getVerticesCount(), numeric_limits<typename Edge::IndexType>::max());
	layers[net.source] = 0;

	// BFS:
	while (!verticesQueue.empty()) {
		typename Edge::IndexType vertex = verticesQueue.front();
		verticesQueue.pop();
	
		if (used[vertex])
			continue;

		used[vertex] = true;
		
		typename Net<Edge>::EdgeIterator iter = net.getConstOuterEdgeIterator(vertex);

		for (iter.setBegin(); !iter.isEnd(); iter.move()) {
			Edge *edge = iter.get();

			if (edge->recidualCapacity() > 0 && layers[edge->from] + 1 <= layers[edge->to]) {
				verticesQueue.push(edge->to);
				
				Edge recidualEdge = *edge;
				recidualEdge.capacity = edge->recidualCapacity();
				recidualEdge.flow = 0;
				layeredNet.addEdge(recidualEdge);
				layers[edge->to] = layers[edge->from] + 1;
			}
		}
	}

	return layeredNet;
}

template <class Edge>
void transwerFlowFromLayeredNetworkToNetwork (Net<Edge> &layeredNet, Net<Edge> &net) {
	// BFS init:
	queue<typename Edge::IndexType> verticesQueue;
	vector<bool> used(net.getVerticesCount(), false);
	verticesQueue.push(net.source);

	// Layered Net init:
	vector<typename Edge::IndexType> layers(net.getVerticesCount(), numeric_limits<typename Edge::IndexType>::max());
	layers[net.source] = 0;

	//cout << "TRANSFER:" << endl;
	//printNet(layeredNet, cout << endl);
	//printNet(net, cout << endl);
	//cout << "Edges:" << endl;

	// BFS:
	while (!verticesQueue.empty()) {
		typename Edge::IndexType vertex = verticesQueue.front();
		verticesQueue.pop();

		if (used[vertex])
			continue;

		used[vertex] = true;
		
		typename Net<Edge>::EdgeIterator iter = net.getConstOuterEdgeIterator(vertex);
		typename Net<Edge>::EdgeIterator &layeredNetIter = layeredNet.getOuterEdgeIterator(vertex);
		layeredNetIter.setBegin();

		for (iter.setBegin(); !iter.isEnd() && !layeredNetIter.isEnd(); iter.move()) {
			Edge *edge = iter.get();

			if (edge->recidualCapacity() > 0 && layers[edge->from] + 1 <= layers[edge->to]) {
				verticesQueue.push(edge->to);

				layers[edge->to] = layers[edge->from] + 1;

				while (layeredNetIter.get()->capacity == 0)
					layeredNetIter.move();

				// GREAT DEBUG:
				//iter.get()->print(cout);
				//layeredNetIter.get()->print(cout << " ");
				//cout << endl;
				
				iter.get()->flow += layeredNetIter.get()->flow;
				iter.get()->reverseEdge->flow -= layeredNetIter.get()->flow;
				layeredNetIter.move();	
			}
		}
	}
}

template <class Edge>
typename Edge::CapacityType findMaxFlow (Net<Edge> &net) {
	typename Edge::CapacityType maxFlow = 0;

	while (true) {
		Net<Edge> layeredNet = buildLayeredNetwork(net);
		typename Edge::CapacityType blockingFlow = findBlockingFlow(layeredNet);

		if (blockingFlow == 0)
			break;

		else {
			transwerFlowFromLayeredNetworkToNetwork(layeredNet, net);
			maxFlow += blockingFlow;
		}
	}

	return maxFlow;
}



// My custom Edge:

template <typename FirstType = unsigned, typename SecondType = long long>
struct Edge {
	typedef FirstType IndexType;
	typedef SecondType CapacityType;
	IndexType from;
	IndexType to;
	CapacityType flow;
	CapacityType capacity;
	Edge *reverseEdge;

	Edge (const IndexType &from = -1, const IndexType &to = -1, const CapacityType &capacity = -1) {
		this->from = from;
		this->to = to;
		this->flow = 0;
		this->capacity = capacity;
	}

	CapacityType recidualCapacity () const {
		return capacity - flow;
	}

	void increaseFlow (const CapacityType &delta) {
		this->flow += delta;
		reverseEdge->flow -= delta;
	}

	Edge getReverse () const {
		return Edge(to, from, 0);
	}

	void print (ostream &os = cout) const {
		os << from << " -> " << to << " (" << flow << "/" << capacity << ")";
	}
};


int main () {
	unsigned verticesCount, edgesCount;
	cin >> verticesCount >> edgesCount;

	Net<Edge<unsigned, long long> > net(verticesCount);
	net.source = 0;
	net.sink = verticesCount - 1;

	for (unsigned i = 0; i < edgesCount; i ++) {
		unsigned from, to;
		long long capacity;
		cin >> from >> to >> capacity;
		net.addEdge(Edge<unsigned, long long>(from, to, capacity));
	}

	//printNet(net, cout << endl);

	cout << findMaxFlow(net) << endl;

	return 0;
}



Measured memory_bytes: 3448