码迷,mamicode.com
首页 > 其他好文 > 详细

网络流之最小费用流

时间:2015-07-24 22:19:40      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

前言:在最大流问题的网络中,给边新加上费用,求流量为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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!