日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图论 —— 网络流 —— 费用流 —— 基于 Dijkstra 的费用流

發布時間:2025/3/17 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图论 —— 网络流 —— 费用流 —— 基于 Dijkstra 的费用流 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【概述】

在求解費用流時,大多數情況都是使用基于 SPFA 的 MCMF 算法,但有時某些毒瘤題會卡 SPFA,此時就要利用基于 Dijkstra 的費用流來求解。

【算法原理】

基于 Dijkstra 的費用流實質上就是在 MCMF 中代替?SPFA 來找增廣路徑,由于 Dijkstra 只能處理負邊權圖,因此在求費用流時,需要對圖進行處理,來保證邊權非負

引入一個勢能函數 h[x],Dijkstra 每進行松弛操作時,加入勢能函數來進行相應的考慮,其引入勢能函數的過程,本質上就是 Johnson 算法對邊重賦值的過程

即對于 u、v?兩點,原來的松弛操作是:dis[u]>dis[v]+w(u,v)

引入勢能函數后有:dis[u]>dis[v]+w(u,v)+h[u]-h[v]

因此只需要保證 w(u,v)+h[u]-h[v] 是正的,那么整張圖就不存在負邊權

下面考慮如何構造勢能函數 h[x]:

設 disG?為原圖第 i 次增廣后的 disG 數組,那么考慮原圖最短路的松弛:對于某一條邊 w(u,v),有:disG[u]>=disG[u]+w(u,v),即 disG[u]-disG[v]>=w(u,v)

因此可令勢能函數 h[u]=disG[u],h[v]=disG[v],其中 disG[u] 為原圖上一次最短路松弛后的 dis 的值

因此,在每一輪的增廣開始時前,h[i] 必須是上一次增廣的 dis[i],故每次增廣完成后,h[i] 要加上 dis[i],這樣 h[i] 就變成了這次增廣的 disG[i],以供下一次增廣使用。

此外,需要注意的是,基于 Dijkstra 的費用流不能用鏈式前向星來寫,要用 vector 來寫,而且在 vector 的結構體中,要多一個維度 rev,以尋找反向弧,這樣的好處是在增廣時可以根據 preV、preE 兩個前趨數組沿匯點回到起點。

【模版】

#define Pair pair<int,int> struct Edge {int to;int cap, dis; //容量、費用int rev; //(u,v)的反向弧中,v在u的位置Edge() {}Edge(int to, int cap, int dis, int rev): to(to), cap(cap), dis(dis), rev(rev) {} }; vector<Edge> G[N]; struct Pre { //記錄前驅int node; //前驅結點int edge; //對應的邊 } pre[N]; int h[N]; //勢能函數 int dis[N]; //費用 void addEdge(int x, int y, int cap, int dis) {G[x].push_back(Edge(y, cap, dis, (int)G[y].size())); //正向邊G[y].push_back(Edge(x, 0, -dis, (int)(G[x].size() - 1))); //反向邊 } bool Dijkstra(int S, int T) {memset(dis, INF, sizeof(dis));dis[S] = 0;priority_queue<Pair, vector<Pair>, greater<Pair>> Q;Q.push(Pair(dis[S], S));while (!Q.empty()) {Pair now = Q.top();Q.pop();int u = now.second;if (dis[u] < now.first)continue;for (int i = 0; i < G[u].size(); i++) {int v = G[u][i].to;int cap = G[u][i].cap;int w = G[u][i].dis;if (cap && dis[v] > w + dis[u] + h[u] - h[v]) {dis[v] = w + dis[u] + h[u] - h[v]; //進行松弛pre[v].node = u; //記錄前驅點pre[v].edge = i; //記錄前驅邊Q.push(Pair(dis[v], v));}}}if (dis[T] == INF)return false;else {for (int i = 0; i <= T + 1; i++) //對于勢能函數,每次加上當前輪的dish[i] += dis[i];return true;} } void maxFlow(int S, int T, int &flow, int &cost) {memset(h, 0, sizeof(h));memset(pre, 0, sizeof(pre));int newFlow = 0; //增廣流量while (flow && Dijkstra(S, T)) { //當無法增廣時,即找到答案int minn = INF;for (int i = T; i != S; i = pre[i].node) {int node = pre[i].node;int edge = pre[i].edge;minn = min(minn, G[node][edge].cap);}flow -= minn; //原流量newFlow += minn; //增廣流量cost += h[T] * minn; //增廣流花銷for (int i = T; i != S; i = pre[i].node) {int node = pre[i].node;int edge = pre[i].edge;int rev = G[node][edge].rev;G[node][edge].cap -= minn;G[i][rev].cap += minn;}} } int main() {int n, m;scanf("%d%d", &n, &m);for (int i = 1; i <= m; i++) {int x, y, cap, cost;scanf("%d%d%d%d", &x, &y, &cap, &cost);addEdge(x, y, cap, cost);}int S = 1, T = n;int flow = INF;//最大流量,根據題目設置int cost = 0;maxFlow(S, T, flow, cost);printf("%d\n", cost);return 0; }

?

新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!

總結

以上是生活随笔為你收集整理的图论 —— 网络流 —— 费用流 —— 基于 Dijkstra 的费用流的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。