给出一个容量网络,那他的最大流一定是一个定值(即使是有多个一样的最大值)。
所以我们从开始的可行流开始增广时,最终的增广量是一定的。
所以为了满足最小费用我们只需要每次找最小费用的增广路即可,直到流量为最大值。
这个问题仅仅是在求增广路时先考虑费用最小的增广路,其他思想和EK思想一样。
我们学过SPFA求最短路算法(bellman-ford的队列优化),所以我们将弧的费用看做是路径长度,即可转化为求最短路的问题了。只需要所走的最短路满足两个条件即可:1剩余流量不为0,2路径变短d[v]>d[u]+cost< u,v> 。
https://www.luogu.org/problem/show?pid=3381#sub
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int n,m,S,T;
int num[100005],cap[100005],cost[100005],nxt[100005],head[5005],cnt=1;//cap记残余流量,cost记费用
int dis[5005],flow[5005],xb[5005];//xb记链表数组的下标,dis用于spfa
int pre[5005];
bool used[5005];
int mflow=0,mcost=0;
void add(int from,int to,int ca,int co)
{
cnt++;//从 2 开始 ,若从0开始,会导致找点时循环停止!!!
num[cnt]=to;
nxt[cnt]=head[from];
head[from]=cnt;
cost[cnt]=co;
cap[cnt]=ca;
}
int bfs(int s,int t)
{
memset(dis,127,sizeof(dis));
memset(used,false,sizeof(used));
for(int i=1;i<=n;i++) pre[i]=-1;
queue <int> q;
while(!q.empty()) q.pop();
q.push(s);
pre[s]=0;
dis[s]=0;
used[s]=true;
flow[s]=dis[0];
while(!q.empty())
{
int k=q.front();
q.pop();
used[k]=false;
for(int i=head[k];i;i=nxt[i])
{
int to=num[i];
if(cap[i]>0&&dis[to]>dis[k]+cost[i])//在spfa判断条件基础上,加上剩余流量>0
{
xb[to]=i;
dis[to]=dis[k]+cost[i];
flow[to]=min(flow[k],cap[i]);
if(!used[to]) used[to]=true,q.push(to);
pre[to]=k;
}
}
}
if(pre[t]==-1) return -1;
else return flow[t];
}
void maxflow(int s,int t)
{
int d;
while(bfs(s,t)!=-1)
{
d=flow[t];
int k=t;
while(k!=s)
{
cap[xb[k]]-=d;
cap[xb[k]^1]+=d;
k=pre[k];
}
mflow+=d;
mcost+=d*dis[t];//每条边上的流量为d,dis[t]为每条边单位流量费用之和
if(d==0) break;
}
return;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&S,&T);
for(int i=1;i<=m;i++)
{
int u,v,w,f;
scanf("%d%d%d%d",&u,&v,&w,&f);
add(u,v,w,f);
add(v,u,0,-f);
}
maxflow(S,T);
printf("%d %d",mflow,mcost);
return 0;
}