最小费用流(沿Dijkstra最短路增广)
最小费用流
最小费用流问题:
在最大流问题的网络中,给边新加上费用,求流量为f时费用的最小值
最大流:残余网络上贪心增广
最小费用流:残余网络上沿着最短路增广
Attention
残余网络中反向边的权值是原边权值的相反数,意味着会有负权边,所以一般用Bellman_Ford求最短路。但它时间复杂度较高,可以考虑导入“势”的概念用Dijkstra求最短路
Dijkstra最短路上增广(模板)
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> P;///first:最短距离 second:节点编号
typedef long long ll;
struct edge{ int to, w, cap, rev; };
int n, m, s, t, flow;
vector<edge> vec[N];
int h[N], dis[N];///节点的势 最短距离
int fro_v[N], fro_e[N];///vector 的 第一维和第二维
///用于确定最短路中点k前面那条边
void add(int from, int to, int w, int cap)
{
int from_sz = vec[from].size(), to_sz = vec[to].size();
vec[from].push_back( (edge){ to, w, cap, to_sz } );
vec[to].push_back( (edge){ from, -w, 0, from_sz } );
}
int min_cost_flow(int s, int t, int f)
{
ll res = 0;
memset(h, 0, sizeof(h));
while(f)///若没完成任务:就对残余网络搜索最短路进行增广
{
priority_queue<P, vector<P>, greater<P> > q;
memset(dis, INF, sizeof(dis));
dis[s] = 0;
q.push(P(0, s));
while(!q.empty())
{
P now = q.top();
q.pop();
int to = now.second;
if(dis[to] < now.first)
continue;
for(int i = 0; i < vec[to].size(); ++i)
{
edge & nxt = vec[to][i];
if(nxt.cap > 0 && dis[nxt.to] > dis[to] + nxt.w + h[to] - h[nxt.to])
{
dis[nxt.to] = dis[to] + nxt.w + h[to] - h[nxt.to];
fro_v[nxt.to] = to;
fro_e[nxt.to] = i;
q.push(P(dis[nxt.to], nxt.to));
}
}
}
if(dis[t] == INF)///还未完成任务(f减为0)就找不出可行流了
return -1;
for(int v = 0; v <= n; ++v)
h[v] += dis[v];
int d = f;///保证f最终为0而非小于0
///即使新的最大流>f, 也只取f
for(int v = t; v != s; v = fro_v[v])
d = min(d, vec[ fro_v[v] ][ fro_e[v] ].cap);
f -= d;
res += d * h[t];///There is a h[t] in Dijkstra, not a dis[t](in Bellman_Ford).
for(int v = t; v != s; v = fro_v[v])///更新残余网络,建反向边
{
edge & nxt = vec[ fro_v[v] ][ fro_e[v] ];
nxt.cap -= d;
vec[v][nxt.rev].cap += d;
}
}
return res;
}
int main()
{
freopen("00in.txt", "r", stdin);
scanf("%d%d%d%d%d", &n, &m, &s, &t, &flow);
int ui, vi, wi, fi;
for(int i = 0; i < m; ++i)
{
scanf("%d%d%d%d", &ui, &vi, &wi, &fi);
add(ui, vi, fi, wi);
}
cout << min_cost_flow(s, t, flow) << '\n';
return 0;
}
详见《挑战程序设计竞赛(第2版)》Page225
本想找个模板题试试,结果模板题都是最小费用最大流