最小費用最大流問題

2021-07-29 12:33:43 字數 3901 閱讀 1705

網路流相關知識參考: 

出處:優you 

大致題意:

給定乙個n*m的地圖,地圖上有若干個man和house,且man與house的數量一致。man每移動一格需花費$1(即單位費用=單位距離),一間house只能入住乙個man。現在要求所有的man都入住house,求最小費用。

解題思路:

費用流問題。

構圖:

把man作為乙個頂點集合u,house作為另乙個頂點集合v,把u中所有點到v中所有點連線,費用cost[u][v]為abs(△x)+abs(△y),反向弧費用cost[v][u]= -cost[u][v],容量cap[u][v]=1,構成乙個多源多匯的二分圖。

由於每乙個多源多匯的網路流都必有乙個與之對應的單源單匯的網路流,為了便於解題,由此構造乙個超級源s和超級匯t,超級源s與u中所有點相連,費用cost[s][u]=0(這是顯然的),容量cap[s][u]=1;v中所有點與超級匯t相連,費用cost[v][t]=0(這是顯然的),容量cap[t][v]=1。

至於其他不連通的點,費用與容量均為0。容量為0的邊,可以理解為飽和邊,不再連通。而上述的所有邊之所以容量初始化為1,是因為每間house只允許入住1個man。而與超級源(匯)相連的邊的費用之所以為0,是為了現在所構造的單源單匯網路流最終所求的最小費用等於原來的多源多匯網路流的最小費用。

求解:

接下來的解題方法有關「最小費用最大流」,請未知的同學先去看看相關文獻。

其實題目所求的最小費用,就是最短距離。用spfa演算法求上述二分圖g的最短路徑,該最短路徑就是圖g的所有增廣鏈中費用最小的一條。比較該增廣鏈上所有邊的容量,最小的容量就是「可分配的最大流maxflow」。

再者就是利用maxflow對增廣鏈上的各條邊的容量進行調整,正向弧容量減去maxflow,反向弧容量加上maxflow。然後該條增廣鏈上各條邊的費用分別乘以maxflow之和,就是第乙個man到達合適的house所花費的最小費用。而圖g經過調整,變成圖g1。

針對圖g1再次使用spfa演算法,找到第二條增廣鏈.....重複上述演算法,直到無法找到增廣鏈為止,則得到的費用和就是所求。

此種解題方法是先計算最小費用,然後再在保證費用最小的情況下,增大流量,最終達到求解目的。理論上,對於有n個man的圖,可以找到n條增廣鏈,即重複迭代spfa演算法n次。若超過n次,則說明該圖存在負權環,無解。但本題並無輸出「無解」的要求,故可認為測試資料中不存在此類資料,因此在迴圈spfa演算法時,不必計算次數,用while()足矣。

最後要注意的是:

discuss上很多人說用了棧空間定義的陣列,提交會re或tle,陣列開大之後才能ac,因此便怪罪於測試資料有誤。

其實不然。真正的原因是,題目所提及的n與m純粹是地圖的行數和列數,與man或house的個數無關,而我看了他們的**,有部分同學卻誤以為n就是man的個數,導致出現re。

因此在此強調一下,題目的n不等於man的個數,建議大家先對地圖的man個數進行數數,得到man的個數,然後再開堆空間(new函式);而非要開棧空間的同學也未嘗不可,由於n與m的值均<=100,且man與house的個數一致,則認為man的個數上限為100*100/2=5000。

//

memory time

//612k 63ms

#include

#include

#include

#include

using namespace std;

struct coordinate

;class

solve

~solve()

}int inf() const

int min(int a,int b)

void input(void

);

void structurebinarymap(void); //

構造二分圖

bool spfa(void); //

搜尋從超級源到超級匯的最短路(即最小費用)的增廣鏈

void addmaxflow(void); //

增廣鏈調整(曾流),計算最小費用

protected

:

int r,c; //

地圖尺寸r*c ,

int n; //

the number of man or houses

int s,t; //

s:超級源編號,t:超級彙編號

int mincost; //

最小花費

char** inmap; //

輸入的地圖

coordinate* m; //

記錄所有man的座標

coordinate* h; //

記錄所有house的座標

int* pre; //

最小費用流路徑上,結點i的前驅結點為pre[i]

int** cost; //

兩點間費用,其中超級源為0,1~n為man,n+1~2n為house,2n+1為超級匯

int** cap; //

兩點間容量,其中超級源為0,1~n為man,n+1~2n為house,2n+1為超級匯

int* dist; //

超級源到各點的最短距離

bool* vist; //

標記各點是否在佇列

};void solve::input(void

) }

return;}

void solve::structurebinarymap(void

)

else

if(inmap[i][j]=='h')

/*建立儲存空間

*/cost=new

int*[2*n+2];

cap=new

int*[2*n+2];

for(i=0;i<2*n+2;i++)

/*初始化超級源s到各個man的容量

*/s=0;

for(i=1;i<=n;i++)

cap[s][i]=1; //

容量預設為1

/*初始化各個man到house的距離(費用)及容量

*/for(i=1;i<=n;i++)

for(j=n+1;j<=2*n;j++)

/*初始化各個house到超級匯t的容量

*/t=2*n+1;

for(j=n+1;j)

cap[j][t]=1; //

容量預設為1

return;}

bool solve::spfa(

void

) }

}q.pop();

//隊頭元素出隊

vist[u]=false

; }

if(dist[t]==inf()) //

dist[t]沒有被調整,說明已不存在增廣鏈

return

false

;

return

true; //

找到一條當前費用和最小的增廣鏈

}void solve::addmaxflow(void

)

return;}

int main(void

)

mysql最小費用最大流問題 最小費用最大流問題

複雜網路中,單源單點的最小費用最大流演算法 mcmf 應用廣泛。在實際網路問題中,不僅考慮從 vs到 vt的流量最大,還要考慮可行流在網路傳送過程中的費用問題,這就是網路的最小費用最大流問題。最小費用最大流問題的一般提法 已知容量網路 d v a c 每條弧 vi,vj 除了已給出容量 cij 外,...

mysql最小費用最大流問題 最小費用最大流問題

最小費用最大流就是在原來求最大流的基礎上,假設每條邊還有乙個單位流量所需要的費用,因為最小費用的出現,原本的平行邊變得有意義,並且允許反向增廣,基本上就是將原本bfs改為進行一次bellmanford演算法尋找最短路徑,只要初始流是該流量下的最小費用可行流,每次增廣後的新流都是新流量下的最小費用流。...

mysql最小費用最大流問題 最小費用最大流

最小費用最大流 修改的dijkstra ford fulksonff演算法 修改的dijkstra其實和johnson演算法的思想是一致的。乙個求最小費用最大流的樸素演算法是這樣的 1求最小費用增廣路2判斷是否存在增廣路,否的話演算法終止。3增加增廣路上邊的流量4在增廣路上新增必要的逆向負權邊5go...