#include
#include
using namespace std;
const int maxint = ~0U>>1;
int n, m, pil, cost = 0;
// n为顶点数,m为边数,pil记录当前增广路径上的消耗,cost记录总消耗
bool v[550];// 记录访问过的顶点
// 边的结构
struct etype{
int t, c, u; // t为该边指向的顶点,c为标签,初始为给定的单位代价,
// c用作能否增广的标记,在增广过程中只有c被标记成0才能增广,这与算法原始描述里修改顶点标记D[i]不太一样
// u为该边的容量(upper limit)
etype * next, * pair; // *next为同一点发出的边的链表,即邻接表,*pair为两个顶点间成对的另一条方向相反的边
etype(){}
etype(int t_, int c_, int u_, etype* next_): t(t_),c(c_),u(u_),next(next_){}
void *operator new(unsigned, void* p){
return p;
} // 不清楚为什么要重载new运算符?
}* e[550]; // e的每个元素都是指向etype的指针,即边etype的数组
// 增广函数,从no顶点开始增广,m为到该顶点时最多能增广的流量,
// 返回用掉的流量,在DFS回溯过程中依次减去用掉的流量。
int aug(int no, int m) {
if(no == n)//当前已到终点,pil为当前增广路径的单位消耗,m为之前增广路径能增广的流量
return cost += pil * m, m;
v[no] = true;// 把当前顶点设为已访问
int l = m; // l用于后面记录剩余流量
// DFS进行增广,for循环里遍历从顶点no发出的每一条边,即遍历顶点no的邻接表
for(etype* i=e[no]; i; i=i->next)
// 如果边i仍有剩余流量,且c被标记为0,满足增广条件,且另一头未被访问
if(i->u && !i->c && !v[i->t]){
// DFS增广下一条边,流量为之前能增广的流量和当前边剩余上界的最小值
// 返回d为用掉的流量
int d = aug(i->t, l < i->u ? l : i->u);
// 当前边剩余容量上界减用掉的d,相反方向的对边加上d,代表可以退回的流量
// 回溯,l减掉用掉的d即为剩余的流量
i->u -= d, i->pair->u += d, l -= d;
// 如果l已经用完,返回用掉的流量m
if(!l) return m;
}
// l为剩下的流量,m-l即为用掉的流量
// 如果DFS达不到终点,则 m-l 为0
return m - l;
}
bool modlabel(){
int d = maxint; // d记录能重标签的最小差距
// 即从已访问过的点到未访问过的点的所有边中,至少改变多少就能使一条边的c标记成0
for(int i=1; i<=n; ++i) // 检查所有顶点
if(v[i]) //从已DFS访问过的顶点出发
for(etype* j=e[i]; j; j=j->next) // 遍历从i出发的所有边,即邻接表
// 如果该边有剩余容量,且未被访问,且该边标记c小于最小差距
if(j->u && !v[j->t] && j->c < d)
d = j->c; // d始终保持重标记c需要的最小差距
if(d == maxint) // 如果已经没法再重标签了,就没法再增广,返回false
return false;
// 将所有从已访问过的点到未访问过的点的边的标记c都减去d,
// 反方向加上d,即重标签过程。
for(int i=1; i<=n; ++i)
if(v[i])
for(etype* j=e[i]; j; j=j->next)
j->c -= d, j->pair->c += d;
// 重标签结束后至少有一条从已访问顶点到未访问顶点的边满足c==0,即可以增广
pil += d; // pil记录增广路径上的单位代价,因为pil每次单调递增最小的差额
// 所以能始终保存增广路径上的单位代价。
return true;
}
int main()
{
freopen("costflow.in","r",stdin);
freopen("costflow.out","w",stdout);
scanf("%d %d",&n,&m);//n为顶点数,m为边数
etype *Pe=new etype[m+m];//正向加反向的边,因此为2m
while(m--) //存储每一条边
{
int s,t,c,u;
scanf("%d%d%d%d",&s,&t,&u,&c);
//与算法原文描述不同,这里用c来作为标记,而不是顶标D[i]
e[s]=new(Pe++)etype(t, c,u,e[s]);//正向边
e[t]=new(Pe++)etype(s,-c,0,e[t]);//反向边
e[s]->pair=e[t];//互为pair
e[t]->pair=e[s];
}
do
do memset(v, 0, sizeof(v));// 每次回溯到源点就把所有顶点访问记录都设为0,即未访问,从头开始
while(aug(1, maxint));// 1代表从源点开始增广,源点流量无穷即有maxint的流量可以增广
while(modlabel()); // 内循环无法增广时就修改标签,这是zkw参考km重标记法重要的思路
// 当重标签无法改变时,循环结束,算法结束。
printf("%d\n",cost);
return 0;
}