#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
ifstream in("maxflow.in");
ofstream out("maxflow.out");
const int nmax = 1001;
const int inf = 1<<30;
class Graf
{
private:
bool orientat;
int nrNoduri;
vector <int> listaAdiacenta[nmax];
vector <pair <int, int>> listaAdiacentaCost[nmax];
//vector <vector<int>> grafRezidual;
public:
Graf(int nrNoduri = 0, bool orientat = false);
void adaugaMuchie(int, int);
void adaugaMuchieCost(int, int, int);
void citireMuchii(int);
void citireMuchiiCost(int);
vector<int> BFS(int);
void DFS(int, bool*);
int nrComponenteConexe();
void DFSStiva(int nodcurent, bool *, stack<int> &);
void sortareTopologica();
void TarjanCTC(int&, int, int*, int*, bool*, stack <int> &, vector<vector<int>> &);
void CTC();
void TarjanBC(int&, int, int, int*, int*, stack <int>&, vector<vector<int>>&);
void BC();
void TarjanMC(int&, int, int, int*, int*, vector<pair<int,int>>&);
void MC();
vector <int> citireHakimi();
bool Hakimi(vector <int>);
vector <int> Dijkstra(int);
pair<int, vector <pair <int, int>>> Prim(int);
vector <int> BellmanFord(int);
void reuniune(int, int, vector<int>&, vector<int>&);
int gasire(int, vector<int>&);
void verifica(int, int, vector<int>&);
void paduri();
bool BFSFMax(int**, int*, int, int);
int EdmondsKarp(int**, int, int);
void citireAfisareEdmondsKarp(int, int, int);
};
Graf::Graf(int nrNoduri, bool orientat) : nrNoduri(nrNoduri), orientat(orientat)
{
this->nrNoduri = nrNoduri;
this->orientat = orientat;
}
void Graf::adaugaMuchie(int nod1, int nod2)
{
listaAdiacenta[nod1].push_back(nod2);
}
bool Graf::BFSFMax(int** grafRezidual, int* parinte, int sursa, int destinatie)
{
queue <int> coadaBFS;
bool vizitatBFS[nmax] = {false};
coadaBFS.push(sursa);
vizitatBFS[sursa] = true;
parinte[sursa] = -1;
while(!coadaBFS.empty())
{
int nodCurent = coadaBFS.front();
coadaBFS.pop();
for(auto vecin:listaAdiacenta[nodCurent])
{
if(!vizitatBFS[vecin] && grafRezidual[nodCurent][vecin] > 0)
{
parinte[vecin] = nodCurent;
vizitatBFS[vecin] = true;
coadaBFS.push(vecin);
if(vecin == destinatie)
{
return true;
}
}
}
}
return false;
}
int Graf::EdmondsKarp(int** grafRezidual, int sursa, int destinatie)
{
int fluxMaximTotal = 0;
int parinte[nmax];
while(BFSFMax(grafRezidual, parinte, sursa, destinatie))
{
int fluxMaximDrum = inf;
int nodCurent = destinatie;
while(nodCurent != sursa)
{
fluxMaximDrum = min(fluxMaximDrum, grafRezidual[parinte[nodCurent]][nodCurent]);
nodCurent = parinte[nodCurent];
}
fluxMaximTotal += fluxMaximDrum;
nodCurent = destinatie;
while(nodCurent != sursa)
{
grafRezidual[parinte[nodCurent]][nodCurent] -= fluxMaximDrum;
grafRezidual[nodCurent][parinte[nodCurent]] += fluxMaximDrum;
nodCurent = parinte[nodCurent];
}
}
return fluxMaximTotal;
}
void Graf::citireAfisareEdmondsKarp(int nrMuchii, int sursa, int destinatie)
{
int nod1, nod2, capacitateRez;
int **grafRezidual = new int*[nmax + 1];
for (int i = 1; i <= nmax; i++)
{
grafRezidual[i] = new int[nmax + 1];
}
for(int i = 0; i < nrMuchii; i++) {
in >> nod1 >> nod2 >> capacitateRez;
adaugaMuchie(nod1, nod2);
grafRezidual[nod1][nod2] = capacitateRez;
adaugaMuchie(nod2, nod1);
grafRezidual[nod2][nod1] = 0;
}
out << EdmondsKarp(grafRezidual, sursa, destinatie);
delete grafRezidual;
}
int main()
{
int n, m;
in >> n >> m;
Graf g(n,true);
g.citireAfisareEdmondsKarp(m,1,n);
return 0;
}