最小費用最大流(minimum cost maximum flow),簡稱費用流(mcmf),是最大流演算法的乙個分支。
現實生活中,如運輸物資,管道輸水等等不僅要考慮最大流量,有時還要考慮運輸費用問題,費用流問題由此而生。在乙個圖中,每條邊不僅有乙個流量值,還有乙個費用值,一般表示是在這條邊上流乙個單位的流量所需的費用,所以對於每一條邊上的流量flow[i],實際花費為flow[i]*cost[i]。另外有些特殊情況一般很難遇見,以後在進行討論。
求費用流的演算法一般可以分為兩類,一是每次都找最大流來更新最小費用直到無法更新,二是每次在剩餘圖中找以費用為權值的最短路來更新流量使其不斷接近最大流直到沒有增廣路。在此筆者推薦第二種方法,並且介紹一種基於spfa的演算法。
q:為什麼不能用dijkstra來找最短路呢?a:反向邊的費用=正向邊的費用的相反數,所以會出現負權邊,用dijkstra這不老臉一黑-_-||
以每條邊的費用作為路徑長度,每次從s到t找一條最短路(當然要滿足路上所有邊的流量》0),最後這條路上的流量就是流量最小的一條邊的流量,不斷重複這個步驟,直到找不到增廣路為止,這時候得到的已有流量就是最大流。
那麼問題來了,怎麼儲存增廣路呢?掌握spfa的同學都知道,結點的最短路值是會不斷更新並加入佇列的,此時我們不能簡單的儲存路徑,而是採用逆序的思想。pre[i]表示i號結點是由第pre[i]條邊走過來,那麼i在路徑中的上乙個點就是from[pre[i]],這樣我們在找到一條增廣路以後,從t開始不斷沿著from[pre[i]]向前找,直到走到s,採用這種覆蓋式的儲存而不是簡單地新增,就有效地避免了存進錯誤的結點或者重複儲存等情況。那為什麼不能直接儲存i號結點的前趨結點,而是存它的前趨邊再由此找到前趨結點呢?我們在處理增廣路的時候,需要更新邊上的流量值。由於採用了陣列模擬鍊錶的形式,很輕鬆的可以知道每條邊對應的那些結點,但是只知道結點很難知道它對應的是哪一條邊。那為什麼不直接使用鄰接矩陣儲存呢?很多網路流圖都是稀疏圖,用鄰接矩陣的話在dfs時複雜度會幾何級增長,所以非常不建議使用,而且陣列模擬鍊錶稍微多寫幾次也很容易上手。具體參看**。
好訊息是,基於這種思想的mcmf演算法**非常簡潔,不需要加入sap或者另外的什麼東西,主過程由兩部分組成,一是spfa,二是處理增廣路,其中spfa也只需加入兩行**即可(已在程式中標示出)。**如下:
#include #include #include #include using namespace std;
const int maxn=10005;
const int maxm=1000005;
int node[maxm],next[maxm],cost[maxm],flow[maxm],head[maxn];
int n,m,ans,pre[maxn],tot,minf,dist[maxn];
bool vis[maxn];
void add(int x,int y,int z,int c)
void init()
ans=0;
}bool spfa()}}
l++;
} return dist[n]!=168430090;
//可以通過判斷dist[n]有沒有被更新過來判斷圖中是否存在增廣路 memset中10等於168430090 見line35
}int main()
cout
}
最小費用最大流
網 絡流的基本問題為 設乙個有向賦權圖g v,e v 其中有兩個特殊的節點s和s s稱為發點,s 稱為收點。圖中各 邊的方向和權數表示允許的流向和最大可能的流量 容量 問在這個網路圖中從發點流出到收點匯集,最大可通過的實際流量為多少?流向的分布情況為怎樣?設有乙個網路圖g v,e v e中的每條邊 ...
最小費用最大流
const int maxn 250 const int maxm 62272 const int inf 0x4ffffff int n,m struct edge edge maxm int head maxn cnt void init void addedge int u,int v,int...
最小費用最大流
看了最小費用最大流的問題,感覺好難啊,想不到,關鍵就是想不到,知道模板什麼用,只是能a題僅此而已,要想學深真的不是件容易的事情啊。為此我總結了以下幾點供複習知識點或者是講課時用 網路流的費用 在實際應用中,與網路流有關的問題,不僅涉及流量,而且還有費用的因素。網路的每一條邊 v,w 除了給定容量ca...