網絡流--最小費用最大流 (理解)


1、什么是最小費用最大流問題

  上篇文章我們講解了最大流問題,那什么是最小費用最大流呢?聽名字就可以看出,我們要在滿足最大流的同時找到達成最大流的最小費用。

對於一個網絡流,最大流是一定的,但是組成最大流的費用是可以不同的,這里就有了在最大流網絡上產生的費用流網絡,就有了最小花費問題。

  簡單來說,就是滿足最大流的路徑可能有多條,我們要從這多條路徑中找到一條花費代價最小的路徑。所以最大流是解決這類問題的前提

 

2、EK算法 + SPFA 最短路

    我們用每條邊單位流量的花費作為邊權,假如一條合法路徑上每條邊的花費分別為 c1,c2,.......ck , 並且這條邊上的最小流量為flow,

  那么這條路徑上的花費為 : c1 * flow + c2*flow + ..... + ck*flow = (c1+ c2 + c3 + .... + ck)*flow =  dis [ci] * flow

  這里的 dis[ci] 就是我們要求的最短路!

 

3、算法思想 

  采用貪心的思想,每次找到一條從源點到達匯點的路徑,增加流量,且該條路徑滿足使得增加的流量的花費最小,直到無法找到一條 從源點到達匯點的路徑,算法結束。
由於最大流量有限,每執行一次循環流量都會增加,因此該算法肯定會結束,且同時流量也必定會達到網絡的最大流量;同時由於每次都是增加的最小的花 費,即當前的最小花費是所有到達當前流量flow時的花費最小值,因此最后的總花費最小。


 

4、求解步驟

  1、找到一條從源點到達匯點的花費最小的路徑,該花費 = 使用該路徑上的邊的單位費用之和
  2、然后找出這條路徑上的邊的容量的最小值f,則當前最大流 max_flow 擴充f(求最大流的過程,同時當前最小費用 min_cost 擴充 f*min_dist(s,t)
  3、更新路徑流量 flow[][] ,將這條路徑上的每條正向邊的容量都減少f,每條反向邊的容量都增加f。
  4、重復(1~3)直到無法找到從源點到達匯點的路徑。
 

5、需要注意幾點

  1、注意超級源點和超級終點的建立。這里的超級源點/匯點的建立是把所有邊統一起來,構成一張網絡圖,因為有些題目不會直接給你網絡圖,

  需要你自己去建立,例如 hdu 1533

  2、初始化時,正向邊的單位流量費用為cost[u][v],那么反向邊的單位流量費用就為 -cost[u][v]。正向邊和反向邊的費用互為相反數,因為回流費用減少。
  3、費用cost數組和容量cap數組每次都要初始化為0。
  4、求解從源點到匯點的“最短”路徑時,由於網絡中存在負權邊,因此使用SPFA來實現。

 

6、代碼

  

int cap[500][500],cost[500][500],flow[500][500];//cap是容量,cost是花費,flow是流量
int pre[500],dis[500],vis[500],cnt[500];//pre是記錄增廣路的前驅節點,dis[i]是記錄源點到節點i的最小距離
//vis[i]標記點是否在隊列中,cnt[i]記錄點i進入隊列的次數

int n,m;
int st,endd;
int mn_cost,mx_flow;
int spfa()
{
    for(int i=0;i<n;i++)
        dis[i]=mx;
    memset(vis,0,sizeof(vis));
    memset(cnt,0,sizeof(cnt));
    
    queue<int>p;
    p.push(st);//st是起點
    vis[st]=1;
    cnt[st]=1;
    dis[st]=0;
    while(!p.empty())
    {
        int now=p.front();
        p.pop();
        vis[now]=0;
        for(int i=0;i<n;i++)
        {
            if(cap[now][i]>flow[now][i]&&dis[i]>dis[now]+cost[now][i])//這里將費用看成是長度,求源點到匯點的最小距離
            {
                dis[i]=dis[now]+cost[now][i];
                pre[i]=now;
                if(vis[i]==0)
                {
                    p.push(i);
                    vis[i]=1;
                    cnt[i]++;
                    if(cnt[i]>n)
                        return 0;
                }
            }
        }
    }
    if(dis[endd]>=mx)
        return 0;
    return 1;
}
void bfs(int n)//頂點數
{
    memset(flow,0,sizeof(flow));
    mx_flow=0;
    mn_cost=0;
    while(spfa())
    {
        int f=mx;
        for(int i=endd;i!=st;i=pre[i])
            f=min(f,cap[pre[i]][i]-flow[pre[i]][i]);

        for(int i=endd;i!=st;i=pre[i])
        {
            flow[pre[i]][i]+=f;
            flow[i][pre[i]]-=f;
        }
        mn_cost+=dis[endd]*f;        
     mx_flow+=f; } }

 

模板題: https://www.cnblogs.com/-citywall123/p/11329609.html


免責聲明!

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



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