#include <iostream>
#include <algorithm>
#include <fstream>
#include <iterator>
#include <limits>
#include <queue>
#include <utility>
#include <vector>
using namespace std;
struct Reweighting {
const vector<vector<int>>& initialCost;
const vector<int>& h;
Reweighting(const vector<vector<int>>& cost,
const vector<int>& h) : initialCost(cost), h(h) {}
int operator()(const int u, const int v) const {
return initialCost[u][v] + h[u] - h[v];
}
};
vector<int> bellman_ford(
const int source,
const vector<vector<int>>& capacity,
const vector<vector<int>>& cost,
const vector<vector<int>>& G);
pair<vector<int>, vector<int>> dijkstra(
const int source,
const vector<vector<int>>& capacity,
const vector<vector<int>>& cost,
const Reweighting& positiveWeights,
const vector<vector<int>>& G);
inline int findResidualCapacity(
int start,
const vector<int>& tree,
const vector<vector<int>>& capacity);
inline int augmentFlow(
int start,
const int residualCapacity,
const vector<int>& tree,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity);
pair<int, int> minCostMaxFlow(
const int source,
const int sink,
vector<vector<int>>& capacity,
vector<vector<int>>& cost,
const vector<vector<int>>& G);
int main() {
ifstream fin("fmcm.in");
ofstream fout("fmcm.out");
int n, m, source, sink;
fin >> n >> m >> source >> sink; source--; sink--;
// residual network
vector<vector<int>> G(n),
capacity(n, vector<int>(n, 0)),
cost(n, vector<int>(n, 0));
for (int u, v, c, z; m; --m) {
fin >> u >> v >> c >> z; u--; v--;
capacity[u][v] = c;
cost[u][v] = z;
cost[v][u] = -z;
G[u].push_back(v);
G[v].push_back(u);
}
pair<int, int> result = minCostMaxFlow(source, sink, capacity, cost, G);
fout << result.first << endl;
return 0;
}
pair<int, int> minCostMaxFlow(
const int source,
const int sink,
vector<vector<int>>& capacity,
vector<vector<int>>& cost,
const vector<vector<int>>& G) {
vector<int> tree;
int minCost = 0, maxFlow = 0;
// reweighting to obtain non-negative costs
// the new weight function produces the same shortest paths
vector<int> h = bellman_ford(source, capacity, cost, G);
while (true) {
// find augmenting paths
pair<vector<int>, vector<int>> t = dijkstra(
source, capacity, cost, Reweighting(cost, h), G);
h.swap(t.first);
tree.swap(t.second);
// if there are no more paths which reach the sink
if (tree[sink] == -1) break;
// check all augmenting paths which end at a neighbour of the sink
for (int u : G[sink]) if (capacity[u][sink]) {
int residualCapacity = findResidualCapacity(u, tree, capacity);
if (residualCapacity == 0) continue;
// augment the flow
residualCapacity = min(residualCapacity, capacity[u][sink]);
capacity[u][sink] -= residualCapacity;
capacity[sink][u] += residualCapacity;
minCost += augmentFlow(u, residualCapacity, tree, cost, capacity);
minCost += cost[u][sink] * residualCapacity;
maxFlow += residualCapacity;
}
}
return make_pair(minCost, maxFlow);
}
inline int findResidualCapacity(
int start,
const vector<int>& tree,
const vector<vector<int>>& capacity) {
int residualCapacity = numeric_limits<int>::max();
for (; start != tree[start] && residualCapacity; start = tree[start])
residualCapacity = min(residualCapacity, capacity[tree[start]][start]);
return residualCapacity;
}
inline int augmentFlow(
int start,
const int residualCapacity,
const vector<int>& tree,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity) {
int residualCost = 0;
for (; start != tree[start]; start = tree[start]) {
capacity[tree[start]][start] -= residualCapacity;
capacity[start][tree[start]] += residualCapacity;
residualCost += cost[tree[start]][start] * residualCapacity;
}
return residualCost;
}
pair<vector<int>, vector<int>> dijkstra(
const int source,
const vector<vector<int>>& capacity,
const vector<vector<int>>& cost,
const Reweighting& positiveWeights,
const vector<vector<int>>& G) {
priority_queue<pair<int, int>> Q;
vector<int> D(G.size(), numeric_limits<int>::max()),
realD(G.size(), numeric_limits<int>::max()),
P(G.size(), -1);
D[source] = realD[source] = 0;
P[source] = source;
Q.push(make_pair(D[source], source));
while (!Q.empty()) {
pair<int, int> next = Q.top(); Q.pop();
int d = next.first, u = next.second;
if (D[u] == d) for (int v : G[u])
if (capacity[u][v] && D[v] > D[u] + positiveWeights(u, v)) {
D[v] = D[u] + positiveWeights(u, v);
realD[v] = realD[u] + cost[u][v];
Q.push(make_pair(D[v], v));
P[v] = u;
}
}
return make_pair(realD, P);
}
vector<int> bellman_ford(
const int source,
const vector<vector<int>>& capacity,
const vector<vector<int>>& cost,
const vector<vector<int>>& G) {
int iterations, n = G.size();
bool ok;
vector<int> D(n, numeric_limits<int>::max());
vector<int> changed, next_changed;
vector<bool> in_changed(n);
D[source] = 0;
changed.push_back(source);
for (iterations = 0, ok = true; iterations <= n && ok; ++iterations) {
ok = false;
next_changed.clear();
fill(in_changed.begin(), in_changed.end(), false);
for (int u : changed) for (int v : G[u])
if (capacity[u][v] && D[v] > D[u] + cost[u][v]) {
ok = true;
D[v] = D[u] + cost[u][v];
if (!in_changed[v]) {
next_changed.push_back(v);
in_changed[v] = true;
}
}
changed.swap(next_changed);
}
return D;
}