题意
给出一个网络,每条边有一个单位费用,如果一条边的单位费用是w,流的量是v,那么这里的费用就是w*v。求出它在最大流的情况下需要的最小费用。
思路
这里用的是ford-fulkerson算法。我们首先按照单位费用来找最短路,因为可能有负边权,所以我这里用的是SPFA。然后用ford-fulkerson算法给每条路更新流量和计算答案。
代码
#include<cstring>
#include<cstdio>
#include<queue>
#define min(a,b) a<b?a:b
using namespace std;
int tot,ans2,ans1,n,m,s,t,ui,vi,wi,fi,head[5001],dis[5001],pre[5001],v[5001];
struct node{
int x,y,next,cost,flow;
//cost代表单位费用,flow代表剩余流量
}e[100001];
void add(int x,int y,int l,int f)
{
e[++tot].x=x;
e[tot].y=y;
e[tot].cost=f;
e[tot].flow=l;
e[tot].next=head[x];
head[x]=tot;
e[++tot].x=y;
e[tot].y=x;
e[tot].cost=-f;
e[tot].flow=0;
e[tot].next=head[y];
head[y]=tot;
}
bool spfa()
{
queue<int> q;
int x,y;
for (int i=0;i<=n;i++)
{
dis[i]=2147483647;
v[i]=0;
}
q.push(s);dis[s]=0;v[s]=1;
while (q.size())
{
x=q.front();q.pop();v[x]=0;
for (int i=head[x];~i;i=e[i].next)
{
if (!e[i].flow) continue;
y=e[i].y;
if (dis[y]>dis[x]+e[i].cost)
{
dis[y]=dis[x]+e[i].cost;//以单位费用做最短路
pre[y]=i;//记录是从哪个点过来的
if (!v[y])
{
v[y]=1;
q.push(y);
}
}
}
}
return dis[t]<2147483647;
}
void addflow()
{
int i=t,mn=2147483647;
while (pre[i])
{
mn=min(mn,e[pre[i]].flow);//找到这条路径上最小的流量
i=e[pre[i]].x;//一直遍历这条路径上的点
}
ans1+=mn;//最大流ans2+=dis[t]*mn;//总费用等于流量*单位费用
i=t;
while (pre[i])
{
e[pre[i]].flow-=mn;//正向边的剩余流量减了
e[pre[i]^1].flow+=mn;//逆向边的流量就加了
i=e[pre[i]].x;
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
tot=1;
memset(head,-1,sizeof(head));
for (int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&ui,&vi,&wi,&fi);
add(ui,vi,wi,fi);
}
while (spfa())//如果源点可以到达汇点
addflow();//我们就一直更新
printf("%d %d",ans1,ans2);
}