#include <iostream>
#include <algorithm>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <queue>
#include <set>
#include <utility>
#include <vector>
using namespace std;
const int INF = numeric_limits<int>::max();
vector<int> bellman_ford(
const int source,
const vector<vector<int>>& cost,
const vector<vector<int>>& capacity,
const vector<vector<int>>& G);
void dijkstra(
vector<int>& tree,
vector<int>& D,
const vector<int>& H,
const int source,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity,
const vector<vector<int>>& G);
int findResidualCapacity(
int start,
const vector<int>& tree,
const vector<vector<int>>& capacity);
int augmentFlow(
const int residualCapacity,
int start,
const vector<int>& tree,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity);
pair<int, int> minCostMaxFlow(
const int source,
const int sink,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity,
const vector<vector<int>>& G);
int main()
{
ifstream fin("fmcm.in");
ofstream fout("fmcm.out");
int nodes, edges, source, sink;
fin >> nodes >> edges >> source >> sink; source--; sink--;
// residual network
vector<vector<int>> G(nodes),
capacity(nodes, vector<int>(nodes, 0)),
cost(nodes, vector<int>(nodes, 0));
for (int u, v, c, z; edges; --edges)
{
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, cost, capacity, G);
fout << result.first << endl;
return 0;
}
pair<int, int> minCostMaxFlow(
const int source,
const int sink,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity,
const vector<vector<int>>& G) {
int minCost = 0, maxFlow = 0, residualCapacity;
vector<int> tree(G.size()); // shortest paths tree
/*
We want to use Dijkstra's algorithm to find the shortest paths.
Because the residual network contains negative cost edges,
we will reweigh the edges to obtain positive weights using
the formula: W(u, v) = cost(u, v) + h(u) - h(v).
If we define h(u) to be the distance from the source to node u, then
from the triangle inequality we have that:
h(v) <= cost(u, v) + h(u) i.e. W(u, v) >= 0.
The distances (h) will have to be recalculated after each iteration.
*/
vector<int> newD(G.size());
// get the distances from the source to each node
vector<int> oldD = bellman_ford(source, cost, capacity, G);
while (true)
{
// find augmenting paths
dijkstra(tree, newD, oldD, source, cost, capacity, G);
// if there are no more paths which reach the sink
if (tree[sink] == -1) break;
oldD.swap(newD);
residualCapacity = findResidualCapacity(sink, tree, capacity);
minCost += augmentFlow(residualCapacity, sink, tree, cost, capacity);
maxFlow += residualCapacity;
}
return make_pair(minCost, maxFlow);
}
int augmentFlow(
const int residualCapacity,
int start,
const vector<int>& tree,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity) {
int c = 0;
for (; start != tree[start]; start = tree[start]) {
capacity[tree[start]][start] -= residualCapacity;
capacity[start][tree[start]] += residualCapacity;
c += cost[tree[start]][start] * residualCapacity;
}
return c;
}
int findResidualCapacity(
int start,
const vector<int>& tree,
const vector<vector<int>>& capacity) {
int flow = INF;
for (; start != tree[start] && flow; start = tree[start])
flow = min(flow, capacity[tree[start]][start]);
return flow == INF ? 0 : flow;
}
void dijkstra(
vector<int>& tree,
vector<int>& D,
const vector<int>& H,
const int source,
const vector<vector<int>>& cost,
vector<vector<int>>& capacity,
const vector<vector<int>>& G) {
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> Q;
vector<int> posD(G.size(), INF);
// fill(D.begin(), D.end(), INF);
fill(tree.begin(), tree.end(), -1);
posD[source] = D[source] = 0;
tree[source] = source;
Q.push(make_pair(0, source));
while (!Q.empty()) {
pair<int, int> t = Q.top(); Q.pop();
int u = t.second, du = t.first;
if (du == posD[u]) for (int v : G[u])
if (capacity[u][v] && posD[v] > posD[u] + (cost[u][v] + H[u] - H[v])) {
posD[v] = posD[u] + (cost[u][v] + H[u] - H[v]);
D[v] = D[u] + cost[u][v];
tree[v] = u;
Q.push(make_pair(posD[v], v));
}
}
}
vector<int> bellman_ford(
const int source,
const vector<vector<int>>& cost,
const vector<vector<int>>& capacity,
const vector<vector<int>>& G) {
vector<int> D(G.size(), INF);
queue<int> Q;
vector<bool> inQ(G.size(), false);
D[source] = 0;
Q.push(source);
inQ[source] = true;
bool has_negative_cycle = false;
while (!Q.empty() && !has_negative_cycle) {
int u = Q.front(); Q.pop();
inQ[u] = false;
for (int v : G[u])
if (capacity[u][v] && D[v] > D[u] + cost[u][v]) {
D[v] = D[u] + cost[u][v];
if (!inQ[v]) {
Q.push(v);
inQ[v] = true;
}
}
}
return D;
}