下面给网络流增加一个因素:费用。假设每条边除了有一个容量限制外,还有一个单位流量所需的费用(cost)。在总流量最大的前提下,总费用最小的流,即最小费用最大流。
在最小费用流问题中,平行边变得有意义了,可能会有两条从u到v 的弧,费用分别为1和2。在没有费用的情况下,可以把两者合并,但由于费用的出现,无法合并这两条弧。再如,若边(u,v)和(v,u)均存在,且费用都是负数。则“同时从u流向v和从v流量u”是个不错的注意。为了更方便的叙述算法,先假定图中不存在平行边和反向边。这样就可以用两个邻接矩阵cap和cost保存各边的容量和费用。为了允许反向增广,规定cap[v][u]=0并且cost[u][v],表示沿着(u,v)的相反方向增广时,费用减少cost[u][v]、
这里直接给出最小费用路算法。和Edmonds-Karp算法类似,但每次用Bellman-Ford算法而非BFS找增广路。只要初始流是该流量下的最小费用可行流。每次增广后的新流都是新流量下的最小费用流。另外,费用值是可负可正的。为了减少溢出的可能,总费用cost采用long long来保存。
首先是辅助宏的定义:
typedef long long int LL;
const int INF = 0x3f3f3f3f;
const int maxn = 100 + 5;
边的定义:
struct Edge
{
int from;
int to;
int cap;
int flow;
int cost; //费用
Edge(int u, int v, int f, int c, int w):from(u), to(v), flow(f), cap(c), cost(w){}
};
struct MCMF
{
int n, m;
vector<Edge> edges; //边数的两倍
vector<int> G[maxn]; //邻接表,G[i][j]表示结点i的第j条边在e数组的序号
int a[maxn]; //当起点到i的可改进量
int p[maxn]; //最短路树上的入弧编号
bool inq[maxn]; //是否在队列中
int d[maxn]; //最小费用,即最短路
void init(int n)
{
this->n = n;
edges.clear();
for(int i = 0; i < n; i++)
G[i].clear();
}
void AddEdge(int from, int to, int cap, int cost)
{
edges.push_back(Edge(from, to, 0, cap, cost));
edges.push_back(Edge(to, from, 0, 0, -cost)); //反向弧
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s, int t, int &flow, LL &cost)
{
int u, i;
memset(d, INF, sizeof(d));
memset(inq, false, sizeof(inq));
d[s] = 0;
p[s] = 0;
a[s] = INF;
queue<int> Q;
Q.push(s);
inq[s] = true;
while(!Q.empty())
{
u = Q.front();
Q.pop();
inq[u] = false;
for(i = 0; i < G[u].size(); i++)
{
Edge &e = edges[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost) //增广
{
d[e.to] = d[u] + e.cost;
a[e.to] = min(a[u], e.cap - e.flow);
p[e.to] = G[u][i];
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to] = true;
}
}
}
}
if(d[t] == INF) //没有通路
return false;
flow += a[t];
cost += (LL)d[t] * a[t];
for(u = t; u != s; u = edges[p[u]].from)
{
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
return true;
}
//需要保证初始网络中没有负权圈
int MincostMaxflow(int s, int t, int flow_limit, LL &cost) //flow_limit:流量限制
{
int flow;
flow = 0;
cost = 0;
while(flow < flow_limit && BellmanFord(s, t, flow, cost));
return flow;
}
};