網絡流之最小費用流


前言:在最大流問題的網絡中,給邊新加上費用,求流量為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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM