Loading docs/graph/flow/min-cost.md +56 −5 Original line number Diff line number Diff line ## 概念 在看这篇文章前请先看 [网络流简介](https://oi-wiki.org/graph/flow/) 这篇 wiki 的定义部分 ## 费用流 给定一个网络 $G=(V,E)$,每条边除了有容量限制 $c(u,v)$,还有一个单位限制 $w(u,v)$ 当 $(u,v)$ 的流量为 $f(u,v)$ 时,需要花费 $f(u,v)\times w(u,v)$. $w$ 也满足斜对称性,即 $w(u,v)=-w(v,u)$. 则该网络中总花费最小的最大流称为**最小费用最大流**,即在最大化 $\sum_{(s,v)\in E}f(s,v)$ 的前提下最小化 $\sum_{(u,v)\in E}f(u,v)\times w(u,v)$. ### 费用 Loading @@ -8,9 +18,50 @@ 网络流图中,花费最小的最大流被称为**最小费用最大流**,这也是接下来我们要研究的对象。 ------ ## MCMF 算法 在最大流的 EK 算法求解最大流的基础上,把**用 BFS 求解任意增广路**改为**用 SPFA 求解单位费用之和最小的增广路**即可 相当于把 $w(u,v)$ 作为边权,在残存网络上求最短路 #### 核心代码 ```cpp struct qxx{int nex,t,v,c;}; qxx e[M]; int h[N],cnt=1; void add_path(int f,int t,int v,int c){e[++cnt]=(qxx){h[f],t,v,c},h[f]=cnt;} void add_flow(int f,int t,int v,int c){add_path(f,t,v,c);add_path(t,f,0,-c);} int dis[N],pre[N],incf[N]; bool vis[N]; bool spfa(){ memset(dis,0x3f,sizeof(dis)); queue<int> q; q.push(s),dis[s]=0,incf[s]=INF,incf[t]=0; while(q.size()){ int u=q.front();q.pop(); vis[u]=0; for(int i=h[u];i;i=e[i].nex){const int &v=e[i].t,&w=e[i].v,&c=e[i].c; if(!w||dis[v]<=dis[u]+c)continue; dis[v]=dis[u]+c,incf[v]=min(w,incf[u]),pre[v]=i; if(!vis[v])q.push(v),vis[v]=1; } } return incf[t]; } int maxflow,mincost; void update(){ maxflow+=incf[t]; for(int u=t;u!=s;u=e[pre[u]^1].t){ e[pre[u]].v-=incf[t],e[pre[u]^1].v+=incf[t]; mincost+=incf[t]*e[pre[u]].c; } } // 调用:while(spfa())update(); ``` ## 求解 ## 类Dinic算法 我们可以在 $\text{Dinic}$ 算法的基础上进行改进,把 $\text{BFS}$ 求分层图改为用 $\text{SPFA}$(由于有负权边,所以不能直接用 $\text{Dijkstra}$)来求一条单位费用之和最小的路径,也就是把 $w(u,v)$ 当做边权然后在残量网络上求最短路,当然在 $\text{DFS}$ 中也要略作修改。这样就可以求得网络流图的**最小费用最大流**了。 Loading @@ -22,7 +73,7 @@ ------ ## 代码 ### 代码 ??? " 最小费用最大流 " Loading Loading
docs/graph/flow/min-cost.md +56 −5 Original line number Diff line number Diff line ## 概念 在看这篇文章前请先看 [网络流简介](https://oi-wiki.org/graph/flow/) 这篇 wiki 的定义部分 ## 费用流 给定一个网络 $G=(V,E)$,每条边除了有容量限制 $c(u,v)$,还有一个单位限制 $w(u,v)$ 当 $(u,v)$ 的流量为 $f(u,v)$ 时,需要花费 $f(u,v)\times w(u,v)$. $w$ 也满足斜对称性,即 $w(u,v)=-w(v,u)$. 则该网络中总花费最小的最大流称为**最小费用最大流**,即在最大化 $\sum_{(s,v)\in E}f(s,v)$ 的前提下最小化 $\sum_{(u,v)\in E}f(u,v)\times w(u,v)$. ### 费用 Loading @@ -8,9 +18,50 @@ 网络流图中,花费最小的最大流被称为**最小费用最大流**,这也是接下来我们要研究的对象。 ------ ## MCMF 算法 在最大流的 EK 算法求解最大流的基础上,把**用 BFS 求解任意增广路**改为**用 SPFA 求解单位费用之和最小的增广路**即可 相当于把 $w(u,v)$ 作为边权,在残存网络上求最短路 #### 核心代码 ```cpp struct qxx{int nex,t,v,c;}; qxx e[M]; int h[N],cnt=1; void add_path(int f,int t,int v,int c){e[++cnt]=(qxx){h[f],t,v,c},h[f]=cnt;} void add_flow(int f,int t,int v,int c){add_path(f,t,v,c);add_path(t,f,0,-c);} int dis[N],pre[N],incf[N]; bool vis[N]; bool spfa(){ memset(dis,0x3f,sizeof(dis)); queue<int> q; q.push(s),dis[s]=0,incf[s]=INF,incf[t]=0; while(q.size()){ int u=q.front();q.pop(); vis[u]=0; for(int i=h[u];i;i=e[i].nex){const int &v=e[i].t,&w=e[i].v,&c=e[i].c; if(!w||dis[v]<=dis[u]+c)continue; dis[v]=dis[u]+c,incf[v]=min(w,incf[u]),pre[v]=i; if(!vis[v])q.push(v),vis[v]=1; } } return incf[t]; } int maxflow,mincost; void update(){ maxflow+=incf[t]; for(int u=t;u!=s;u=e[pre[u]^1].t){ e[pre[u]].v-=incf[t],e[pre[u]^1].v+=incf[t]; mincost+=incf[t]*e[pre[u]].c; } } // 调用:while(spfa())update(); ``` ## 求解 ## 类Dinic算法 我们可以在 $\text{Dinic}$ 算法的基础上进行改进,把 $\text{BFS}$ 求分层图改为用 $\text{SPFA}$(由于有负权边,所以不能直接用 $\text{Dijkstra}$)来求一条单位费用之和最小的路径,也就是把 $w(u,v)$ 当做边权然后在残量网络上求最短路,当然在 $\text{DFS}$ 中也要略作修改。这样就可以求得网络流图的**最小费用最大流**了。 Loading @@ -22,7 +73,7 @@ ------ ## 代码 ### 代码 ??? " 最小费用最大流 " Loading