最小费用最大流 模版

一、最小费用最大流的模型
保证流量最大的前提下,所需的费用最小,这就是最小费用最大流问题.


 

带有费用的网络流图: G=(V,E,C,W)
V:顶点; E:弧;C:弧的容量;W:单位流量费用。
任意的弧<i,j>对应非负的容量c[i,j]和单位流量费用w[i,j]。满足:
① 流量f是G的最大流。
② 在f是G的最大流的前提下,流的费用最小。

 

F是G的最大流的集合(最大流不止一个):

最小费用最大流,模板

在最大流中寻找一个费用最小的流 f.

 

二、最小费用最大流的算法
基本思路:
    把弧<i,j>的单位费用w[i,j]看作弧<i,j>的路径长度,每次找从源点s到汇点t长度最短(费用最小)的可增广路径进行增广。
1. 最小费用可增广路
2. 路径s到t的长度即单位流量的费用。

ps:是网络流EK算法的改进,在求增广路径的时候,把bfs改为带权的spfa,每次求权值最小的增广路。

ps:要注意一点,逆边cost[i][j] = -cost[j][i],不能忘了加上去。

 

 

 1 自己的模板:邻接矩阵。
 2 
 3 #include<iostream>
 4  using  namespace std;
 5 
 6  
 7 
 8  int n, ans;
 9  int cap[Max][Max], pre[Max];
10  int cost[Max][Max], dis[Max];
11  int que[Max];
12  bool vis[Max];
13 
14  
15 
16  bool spfa(){                   //   源点为0,汇点为n。
17       int i, head =  0, tail =  1;
18      for(i =  0; i <= n; i ++){
19         dis[i] = inf;
20         vis[i] =  false;
21     }
22     dis[ 0] =  0;// dis 表示 最小 花费
23     que[ 0] =  0;
24 
25     vis[u] =  true;
26 
27      while(tail != head){       //   循环队列。
28           int u = que[head];
29 
30          for(i =  0; i <= n; i ++)
31              if(cap[u][i] && dis[i] > dis[u] + cost[u][i]){     //   存在路径,且权值变小。
32                  dis[i] = dis[u] + cost[u][i];
33                 pre[i] = u;
34                  if(!vis[i]){
35                     vis[i] =  true;
36                     que[tail ++] = i;
37                      if(tail == Max) tail =  0;
38                 }
39             }
40         vis[u] =  false;
41         head ++;
42          if(head == Max) head =  0;
43     }
44      if(dis[n] == inf)  return  false;
45      return  true;
46 }
47 
48  
49 
50  void end(){
51      int i, sum = inf;
52      for(i = n; i !=  0; i = pre[i])
53         sum = min(sum, cap[pre[i]][i]);
54      for(i = n; i !=  0; i = pre[i]){
55         cap[pre[i]][i] -= sum;
56         cap[i][pre[i]] += sum;
57         ans += cost[pre[i]][i] * sum;    //   cost[][]记录的为单位流量费用,必须得乘以流量。
58      }
59 }
60 
61  
62 
63  int main(){
64     ....
65     ans =  0;
66      while(spfa()) end();
67     ....
68      return  0;
69 }
 1 自己的模板:邻接表。
 2 
 3 #include<iostream>
 4  using  namespace std;
 5 
 6  
 7 
 8  struct{
 9      int v, cap, cost, next, re;     //   re记录逆边的下标。
10  }edge[eMax];
11  int n, m, ans;
12  int k, edgeHead[nMax];
13  int que[nMax], pre[nMax], dis[nMax];
14  bool vis[nMax];
15 
16  
17 
18  void addEdge( int u,  int v,  int ca,  int co){
19     edge[k].v = v;
20     edge[k].cap = ca;
21     edge[k].cost = co;
22     edge[k].next = edgeHead[u];
23     edge[k].re = k +  1;
24     edgeHead[u] = k ++;
25     edge[k].v = u;
26     edge[k].cap =  0;
27     edge[k].cost = -co;
28     edge[k].next = edgeHead[v];
29     edge[k].re = k -  1;
30     edgeHead[v] = k ++;
31 }
32 
33  
34 
35  bool spfa(){                   //   源点为0,汇点为n。
36       int i, head =  0, tail =  1;
37      for(i =  0; i <= n; i ++){
38         dis[i] = inf;
39         vis[i] =  false;
40     }
41     dis[ 0] =  0;
42     que[ 0] =  0;
43 
44     vis[u] =  true;
45      while(tail > head){        //   这里最好用队列,有广搜的意思,堆栈像深搜。
46           int u = que[head ++];
47 
48          for(i = edgeHead[u]; i !=  0; i = edge[i].next){
49              int v = edge[i].v;
50              if(edge[i].cap && dis[v] > dis[u] + edge[i].cost){
51                 dis[v] = dis[u] + edge[i].cost;
52                 pre[v] = i;
53                  if(!vis[v]){
54                     vis[v] =  true;
55                     que[tail ++] = v;
56                 }
57             }
58         }
59         vis[u] =  false;
60     }
61      if(dis[n] == inf)  return  false;
62      return  true;
63 }
64 
65  
66 
67  void end(){
68      int u, p, sum = inf;
69      for(u = n; u !=  0; u = edge[edge[p].re].v){
70         p = pre[u];
71         sum = min(sum, edge[p].cap);
72     }
73      for(u = n; u !=  0; u = edge[edge[p].re].v){
74         p = pre[u];
75         edge[p].cap -= sum;
76         edge[edge[p].re].cap += sum;
77         ans += sum * edge[p].cost;      //   cost记录的为单位流量费用,必须得乘以流量。
78      }
79 }
80 
81  
82 
83  int main(){
84 
85     ...
86 
87     ans =  0;
88      while(spfa()) end();
89     ...
90 
91      return  0;
92 }
93 
94 

转载于:https://www.cnblogs.com/acSzz/archive/2012/05/03/2481400.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值