spfa费用流java_EK-SPFA费用流 总结+模板——【【模板】最小费用最大流】

【用途】每一条边增加一个参数:每个单位流量的费用;求解在最大流的情况下最小的费用。

【主要思路】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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值