费用流 (学习笔记)(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&&sum; 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;
}
posted @ 2026-01-25 20:43  Yuriha  阅读(28)  评论(0)    收藏  举报