给出一个容量网络,那他的最大流一定是一个定值(即使是有多个一样的最大值)。
所以我们从开始的可行流开始增广时,最终的增广量是一定的。
所以为了满足最小费用我们只需要每次找最小费用的增广路即可,直到流量为最大值。
这个问题仅仅是在求增广路时先考虑费用最小的增广路,其他思想和EK思想一样。
我们学过SPFA求最短路算法(bellman-ford的队列优化),所以我们将弧的费用看做是路径长度,即可转化为求最短路的问题了。只需要所走的最短路满足两个条件即可:1剩余流量不为0,2路径变短d[v]>d[u]+cost< u,v> 。
#include <iostream>
#include <queue>
#include <string.h>
#include <cstdio>
using namespace std;
int maxData = 0x7fffffff;
queue<int> dl;
int head[5001],cost[100001],net[100001],to[100001],cap[100001];//cost为费用数组,cap为容量
int cnt=1;
void add(int x,int y,int c,int z)
{
to[++cnt]=y;
cost[cnt]=z;
cap[cnt]=c;
net[cnt]=head[x];
head[x]=cnt;
}
int flow[5001];
int pre[5001];//前驱节点
int xb[5001];//记录下标,便于修改容量
int n,m;
int mflow=0;//最大流
int mcost=0;//最小费用
int dis[5001];//记录从源点到当前节点的最小的费用值,学过最小路的都懂
int f[5001];//标记是否在队列中
int BFS(int s,int t)
{
memset(dis,127,sizeof(dis));//赋初值最大值
memset(f,0,sizeof(f));//赋值为0
int INF=dis[0];
while(!dl.empty())
dl.pop();
for(int i=1;i<=n;i++)
pre[i]=-1;//清空前驱节点
f[s]=1;
dis[s]=0;
pre[s]=0;
flow[s]=maxData;
dl.push(s);
while(!dl.empty())
{
int dd=dl.front();
dl.pop();
f[dd]=0;
for(int i=head[dd];i;i=net[i])
{
int tmp=to[i];
if(cap[i]>0&&dis[tmp]>dis[dd]+cost[i])//松弛操作
{
dis[tmp]=dis[dd]+cost[i];
pre[tmp]=dd;
xb[tmp]=i;
flow[tmp]=min(flow[dd],cap[i]);
if(!f[tmp]) f[tmp]=1,dl.push(tmp);
}
}
}
if(dis[t]>=INF) return 0;//成功找到了增广路
return 1;
}
void max_flow(int s,int t)
{
while(BFS(s,t))
{
int k=t;
while(k!=s)
{
cap[xb[k]]-=flow[t];
cap[xb[k]^1]+=flow[t];
k=pre[k];
}
mflow+=flow[t];
mcost+=flow[t]*dis[t];//流量乘以最小单位流量的费用即为最小费用
}
}
int main()
{
int s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
int x,y,c,d;
scanf("%d%d%d%d",&x,&y,&c,&d);
add(x,y,c,d);
add(y,x,0,-d);//建立反向边,cost的值为正向边的负值
}
max_flow(s,t);
printf("%d %d",mflow,mcost);
}