费用流 (学习笔记)(26.1.25)
费用流 (学习笔记)
概述
在网络流上增加单位流量费用的网络,可以借助此解决一些既有容量的限制和权值的要求的网络(建模)问题
最大流最小费用
对于最大流,就是正常算法没什么好说的,对于最小费用,我们需要思考一下,在保证最大流的时候怎么做到最小费用
在对于两条相同的路径,其上方的入流有限都为 \(k\) 那么我们发现,选择所需费用更小的一条路径是更优的,那么对于更小路径,我们自然想到了最短路算法,而其实现也是使用 BFS ,所以我们可以使用最短路进行建图并且跑一个 dinic ,这样子就可以实现最大流最小费用
实现
主要注意对于负环的判断(所以用 spfa 更好一些),还有建图的部分,其余没有什么难的。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 3e5;
const i64 inf = 1e17;
int n, m, s, t;
int now[N], h[N], cnt=1;
i64 dis[N];
struct node{
int to, nxt;
i64 w, c;
}e[N];
void add(int u, int v, i64 w, i64 c){
e[++cnt] = {v,h[u],w,c};h[u]=cnt;
e[++cnt] = {u,h[v],0,-c};h[v]=cnt;//在建反边的时候,需要注意的是,我们实现的是一种反悔的操作,所以权值要还回去
}
struct Node{
int w, num;
bool operator<(const Node &d) const {return d.w<w;}
};
priority_queue<Node> q;
int vis[N];
bool spfa(){
for(int i=1; i<=n; i++) vis[i]=0, dis[i]=inf, now[i]=h[i];
queue<int> q;
q.push(s);
vis[s]=1, dis[s]=0;
while(!q.empty()){
int u = q.front();
q.pop();
vis[u]=0;
for(int i=h[u]; i; i=e[i].nxt){
int v = e[i].to;
if(e[i].w>0&&dis[v]>dis[u]+e[i].c){
dis[v] = dis[u]+e[i].c;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
return dis[t]!=inf;
}
i64 Cost=0;
i64 dfs(int u, i64 sum){
if(u==t) return sum;
i64 res=0;
vis[u]=1;//这个是为了防止负环,习惯性写一下总是好的
for(int i=now[u]; i&∑ i=e[i].nxt){
now[u]=i;
int v = e[i].to;
if(!vis[v]&&e[i].w>0&&dis[v]==dis[u]+e[i].c){
i64 k = dfs(v,min(sum,e[i].w));
if(!k) dis[v]=inf;
e[i].w-=k, e[i^1].w+=k;
res+=k, sum-=k;
Cost+=k*e[i].c;//在选择之后直接计算cost
}
}
vis[u]=0;
return res;
}
i64 ans=0;
int main(){
cin>>n>>m>>s>>t;
for(int i=1; i<=m; i++){
int u, v;i64 w, c;
cin>>u>>v>>w>>c;
add(u,v,w,c);
}
while(spfa()) {
for(int i=1; i<=n; i++) vis[i]=0;
ans+=dfs(s,inf);
}
cout<<ans<<' '<<Cost;
return 0;
}

浙公网安备 33010602011771号