标签:
前言:在最大流问题的网络中,给边新加上费用,求流量为F时费用的最小值。该类问题就是最小流费用问题。
算法分析:在解决最小费用流的问题上,我们将沿着最短路增广并以费用作为路径长短的衡量,在增广时残余网络中的反向边的费用应该是原边费用的相反数,目的是保证过程可逆并且正确。因此在本算法的实现上,其一我们需要利用Bellman_Ford或Dijkstra算法求得最短路并将其保存,其二则是求解该通过该最短路的实际流量并且修改相关数据,以便下一次在残余网络上继续增广。如果采用Bellman_Ford求最短路,时间复杂度为O(FVE);如果采用Dijkstra,时间复杂度为O(FElogV)。
算法正确性的证明:
(1)设通过上述算法求得的费用流为f并假设存在流量相同但费用更小的流f‘。已知流f和f‘中,除了s与t以外,其它顶点的流入量均等于流出量。则在流f‘—f中,由于f与f‘的流量相同且f‘的费用小于f,所以流f‘—f是由若干圈组成的且至少存在一个负圈。那么我们可以得到这样一个结论:如果f是最小费用流,则网络中没有负圈,反之亦成立。
(2)利用(1)中的结论,我们将通过归纳法证明。设f(i)是流量为i的所有流中费用最小的流,则当i=0时,f(i)=0,对应的残余网络便是原图,如果原图不含负圈则f(0)便是流量为0的最小费用流。假设流量为i时,f(i)是最小费用流,并且已经通过算法求得流量为i+1时的费用为f(i+1),则f(i+1)—f(i)是f(i)对应的残余网络中s-t的最短路。
(3)假设f(i+1)不是最小费用流,即存在费用更小的流f‘(i+1)。在流f‘(i+1)—f(i)中,除s和t以外,其它顶点的流入量均等于流出量,因而是一条s到t的路径和若干圈组成的。已知
f(i+1)—f(i)是f(i)对应的残余网络中s-t的最短路,而f‘(i+1)费用比f(i+1)更小,所以f‘(i+1)—f(i)中至少含有一个负圈即f(i)对应的残余网络中含有一个负圈。此结果与f(i)是最小费用流矛盾,故假设不成立即f(i+1)是最小费用流。
最小费用流算法实现:
//Bellman—Ford算法
//Bellman算法求最短增广路&最小费用流 O(FEV)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define MV 100
#define INF (1 << 20)
struct edge
{
int t, cap, cost, rev;
edge(int to = 0, int c = 0, int ct = 0, int r = 0): t(to), cap(c), cost(ct), rev(r) {};
};
vector <edge> G[MV];//图的邻接表表示
int dis[MV];//单源点s到其它顶点的最短距离(本次搜索的最小费用)
int prevv[MV], preve[MV];//最短路中的前驱结点 对应边
int min_cost_flow(int n, int v, int s, int t, int f)
{
int ans = 0, i, j; //最小费用
while(f > 0)
{
fill(dis, dis + v, INF);
dis[s] = 0
bool update = true;
while(update)
{//bellman算法求解s-t可达路径中的最短路(费用最少)且是否可达由e.c是否大于0决定
update = false;
for(i = 0; i < v; ++i)
{
int size = G[i].size();
if(dis[i] == INF)
continue;
for(j = 0; j < size; ++j)
{
edge &es = G[i][j];
if(es.cap > 0 && dis[es.t] > dis[i] + es.cost)
{
dis[es.t] = dis[i] + es.cost;
prevv[es.t] = i;//路径还原
preve[es.t] = j;
update = true;
}
}
}
}
if(dis[t] == INF)
return -1;
int d = f; //d:本次求得的最小费用流
for(i = t; i != s; i = prevv[i])
d = min(d, G[prevv[i]][preve[i]].cap);
ans += d * dis[t];
f -= d;
for(i = t; i != s; i = prevv[i])
{
edge &es = G[prevv[i]][preve[i]];
es.cap -= d;
G[es.t][es.rev].cap += d;
}
}
return ans;
}
void solve(int n, int v, int s, int t, int f)
{
int i, j;
for(i = 0; i < n; ++i)
{//建图
int s1, t1, cap, cost;
cin >> s1 >> t1 >> cap >> cost;
G[s1].push_back(edge(t1, cap, cost, G[t1].size()));
G[t1].push_back(edge(s1, 0, -cost, G[s1].size() - 1));
}
cout << min_cost_flow(n, v, s, t, f) << endl;
}
int main()
{
int n, v, s, t, f;//n条边 v个顶点 源点s 汇点t 传输的流量f
cin >> n >> v >> s >> t >> f;
solve(n, v, s, t, f);
return 0;
}
//最小费用流Dijkstra算法
//Dijkstra算法求最小费用流核心代码:
//h[MAX_V]:导入势保证所有边均为非负边 O(FElogV)
int min_cost_flow(int n, int v, int s, int t, int f)
{
int i, ans = 0;
fill(h, h + v, 0);
while(f > 0)
{
//Dijkstra算法:寻找最短路 O(ElogV)
priority_queue<P, vector<P>, greater<P> > que;
fill(dis, dis + v, INF);
dis[0] = 0;
que.push(P(0, s));
while(!que.empty())
{
P p = que.top();
que.pop();
int v = p.second;
if(dis[v] < p.first)
continue;
int size = G[v].size();
for(i = 0; i < size; ++i)
{
edge es = G[v][i];//****
if(es.cap > 0 && dis[es.to] > dis[v] + es.cost + h[v] - h[es.to])
{
dis[es.to] = dis[v] + es.cost + h[v] - h[es.to];
prevv[es.to] = v;
preve[es.to] = i;
que.push(P(dis[es.to], es.to));
}
}
}
if(dis[t] == INF)
return -1;
//更新势
for(i = 0; i < v; ++i)
h[i] += dis[i];
int d = f;
for(i = t; i != s; i = prevv[i])
d = min(d, G[prevv[i]][preve[i]].cap);
ans += d * h[t];
f -= d;
for(i = t; i != s; i = prevv[i])
{
edge &es = G[prevv[i]][preve[i]];
es.cap -= d;
G[i][es.rev].cap += d;
}
}
return ans;
}
标签:
原文地址:http://www.cnblogs.com/aizi/p/4674660.html