%%%xtx的代码了好久,又翻了很多文章,终于算是差不多看懂了???
费用流问题,是建立在最大流问题基础之上的,也就是说,现在流要钱了,我们要求出最大流,可能有很多个最大流,我们还要费用最小的那个。比较直观的一个想法便是:把原来的bfs分层变成spfa等最短路算法,求出从s到t费用最小的一条路径来增广。然而这样我们就失去了Dinic的一个优势,即多路增广。因为每次我们进行增广后,最短路都不一定再满足了,我们需要重新运行spfa来算。这样就比较慢。zkw费用流改进在哪里呢?他使用了类似km算法的每次修改顶标的做法。
任何一个最短路算法保证, 算法结束时对任意指向顶点 i 、从顶点 j 出发的边满足 Di≤Dj+cij (条件1), 且对于每个 i 存在一个 j 使得等号成立 (条件2). 换句话说, 任何一个满足以上两个条件的算法都可以叫做最短路, 而不仅仅是 SPFA、Dijkstra, 算法结束后, 恰在最短路上的边满足 Di=Dj+cij .
在最小费用流的计算中, 我们每次沿 Di=Dj+cij 的路径增广后都不会破坏条件 1, 但是可能破坏了条件 2. 不满足条件 2 的后果是什么呢? 使我们找不到每条边都满足 Di=Dj+cij 新的增广路. 只好每次增广后使用 Dijkstra, SPFA 等等算法重新计算新的满足条件 2 的距离标号. 这无疑是一种浪费. KM 算法中我们可以通过不断修改可行顶标, 不断扩大可行子图, 这里也同样, 我们可以在始终满足条件 1 的距离标号上不断修改, 直到可以继续增广 (满足条件 2).
Di+cij≥Dj ⇔ Di−Dj+cij≥0 ①
Di+cij=Dj ⇔ Di−Dj+cij=0 ②
对于一个顶标D,我们可以不断的dfs找Di−Dj+cij=0的增广路经
假设我们当前dfs失败
即使失败还是有一些点能满足Di−Dj+cij=0的
这些点被我们当前dfs到了
我们记这些点的点集为 V
找到 Δ=min{Di−Dj+cij}|i∈V,j∉V,flowij>0
然后我们对 ∀i∈V,Dπi=Di−Δ
证明:
弧(i,j)可以分成四类,再根据当前dfs失败的条件,有:
i∈V,j∉V 原来Di−Dj+cij≥Δ>0 新图 Dπi−Dj+cij≥0
i∈V,j∈V 原来Di−Dj+cij=0 新图 Dπi−Dπj+cij=0
i∉V,j∉V 原来Di−Dj+cij≥0 新图 Di−Dj+cij≥0
i∉V,j∈V 原来Di−Dj+cij≥0 新图 Di−Dπj+cij>=Δ
可以发现第一类弧中一定有至少一条满足
原来Di−Dj+cij=Δ 新图 Dπi−Dj+cij=0
即至少有一条新的边进入了 Dj=Di+cij 的子图。
这样直到找不到这样的 Δ ,就不会再有增广路了。
本来我们要记录D数组,每次修改距离标记改的是D数组,但是我们也可以不计这个D数组,每条边(u,v)的c就表示 Du−Dv+cuv ,则我们每次减正向边,加反向边,也是一样的效果。那么在最短路径上的边,也就是c为0的边。
#include
using namespace std;
#define ll long long
#define N 5010
#define inf 0x3f3f3f3f
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m,s,t,h[N],num=1,cost=0,price=0,mxflow=0;
bool vis[N];
struct edge{
int to,next,w,c;
}data[50010<<1];
inline void add(int x,int y,int w,int c){
data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].w=w;data[num].c=c;
data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].w=0;data[num].c=-c;
}
inline int dinic(int x,int low){
vis[x]=1;if(x==t){cost+=low*price;mxflow+=low;return low;}int tmp=low;
for(int i=h[x];i;i=data[i].next){
int y=data[i].to;if(vis[y]||!data[i].w||data[i].c) continue;
int res=dinic(y,min(tmp,data[i].w));
tmp-=res;data[i].w-=res;data[i^1].w+=res;
if(!tmp) return low;
}return low-tmp;
}
inline bool label(){
int d=inf;
for(int x=1;x<=n;++x){
if(!vis[x]) continue;
for(int i=h[x];i;i=data[i].next){
int y=data[i].to;if(vis[y]) continue;
if(data[i].w&&data[i].cif(d==inf) return 0;
for(int x=1;x<=n;++x){
if(!vis[x]) continue;
for(int i=h[x];i;i=data[i].next)
data[i].c-=d,data[i^1].c+=d;
}price+=d;return 1;
}
int main(){
// freopen("a.in","r",stdin);
n=read();m=read();s=read();t=read();
while(m--){
int x=read(),y=read(),w=read(),c=read();add(x,y,w,c);
}do
do memset(vis,0,sizeof(vis));while(dinic(s,inf));
while(label());
printf("%d %d\n",mxflow,cost);
return 0;
}