与最大流不同之处在于,每条弧不只有流量,还添加了费用
流过每条边的费用为这条边的流量×费用
一般问题是最小费用最大流,即在最大流的前提下要求总费用最小
众所周知:(spfa) 它还能续一命
(dij)是处理不了负边权的,当然如果您是(NOI2018)现场爷并且有某些心里阴影的话,那您应该不会看我的博客学费用流啊
网上有些题解说是(dinic)算法魔改一下,把(BFS)改成(spfa)就好了,但是其实还是有点区别的吧
的确费用流和(dinic)都是多路增广但是由于费用流要兼顾最小费用的原则,每次增广的优先条件是费用最小,而非流量最大
当然这也是在有增广路的前提下,也就是说每次增广流量必定在增大
所以费用流的复杂度更加选学,它并不是多项式级别的,理论上界应该是(O(nmc)),其中(c)是最大流的流量
对反向边我们需要增加一些操作:将反向边费用设为负的,这样才能满足流过反向边完全抵消正向边的影响
代码是以前写的,格式和现在有所不同,不过还算规范
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,st,ed;
int maxf,minv;
int c[10010],dis[10010];
bool vis[10010];
int pre[10010],eg[10010];
int head[10010],cnt=1;
struct point
{
int nxt,to,val,c;
}a[100010];
inline void add(int x,int y,int v,int f)
{
a[++cnt].nxt=head[x];
a[cnt].to=y;;
a[cnt].val=v;
a[cnt].c=f;
head[x]=cnt;
}
queue<int> q;
inline bool spfa()
{
memset(c,0x3f,sizeof(c));
memset(dis,0x3f,sizeof(dis));dis[st]=0;
memset(vis,0,sizeof(vis));vis[st]=1;
q.push(st);pre[ed]=0;
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i;i=a[i].nxt)
{
int t=a[i].to;
if(a[i].c&&dis[t]>dis[now]+a[i].val)
{
pre[t]=now;
eg[t]=i;
dis[t]=dis[now]+a[i].val;
c[t]=min(c[now],a[i].c);
if(!vis[t])
{
vis[t]=1;
q.push(t);
}
}
}
}
return pre[ed];
}
inline void dinic()
{
while(spfa())
{
int now=ed;
maxf+=c[ed];
minv+=c[ed]*dis[ed];
while(now!=st)
{
a[eg[now]].c-=c[ed];
a[eg[now]^1].c+=c[ed];
now=pre[now];
}
}
}
signed main()
{
n=read(),m=read(),st=read(),ed=read();
for(int x,y,z,v,i=1;i<=m;++i)
{
x=read(),y=read(),z=read(),v=read();
add(x,y,v,z);
add(y,x,-v,0);
}
dinic();
printf("%lld %lld
",maxf,minv);
return 0;
}
费用流一般用法是构造最大流然后求最小费用