【用途】每一条边增加一个参数:每个单位流量的费用;求解在最大流的情况下最小的费用。
【主要思路】EK算法,即SPFA+增广路
在图上 以费用为边权 跑SPFA:在更新的过程中,如果更新成功,那么顺便进行往下一个结点尽可能多地流水,并且把流水的路径记录下来(pre[v]=u->v的边)。
跑完SPFA后,顺着之前记录的路径从汇点溯回到源点,并且进行增广路。
最小的总费用就是累计 (当前路径所流总量) x (s到t的最短路径)
【参考程序】
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
int cur=1,n,m,s,t,mcost,mflow;
int head[5005],dis[5005],flow[5005],pre[5005];
struct EDGE{
int t,next,w,f;
}e[100005];
void add(int a,int b,int w,int f)
{
cur++;e[cur].t=b;e[cur].next=head[a];e[cur].w=w;e[cur].f=f;head[a]=cur;
cur++;e[cur].t=a;e[cur].next=head[b];e[cur].w=0;e[cur].f=-f;head[b]=cur;
}
queue < int > q;
bool vis[5005];
bool SPFA(int s,int t)
{
memset(dis,INF,sizeof dis);
memset(vis,0,sizeof vis);
dis[s]=0;
vis[s]=1;
flow[s]=INF;
q.push(s);
while (!q.empty())
{
int u=q.front();q.pop();
vis[u]=false;
for (int h=head[u];h!=-1;h=e[h].next)
{
int v=e[h].t,f=e[h].f;
if (e[h].w&&dis[u]+f
{
dis[v]=dis[u]+f;//更新最短路径
flow[v]=min(flow[u],e[h].w);//尽可能地流水
pre[v]=h;//记录路径
if (!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
return dis[t]!=INF;
}
void Update(int s,int t)
{
int x=t;
while (x!=s)
{
int i=pre[x];
e[i].w-=flow[t];
e[i^1].w+=flow[t];
x=e[i^1].t;
}//沿着记录下的路径并进行增广路
mflow+=flow[t];
mcost+=flow[t]*dis[t];//累计费用
}
void E_K(int s,int t)
{
while (SPFA(s,t))//当还有多余流量时
Update(s,t);
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
memset(head,-1,sizeof head);
for (int i=1;i<=m;i++)
{
int a,b,w,f;
scanf("%d%d%d%d",&a,&b,&w,&f);
add(a,b,w,f);
}
E_K(s,t);
printf("%d %d\n",mflow,mcost);
return 0;
}