网络流之费用最小最大流
前不久看了许久的费用流,也没看懂,然后看了农民伯伯的一篇论文后就忽然明白了,十分简单。
最小费用最大流
通过EK,Dinic,ISAP算法可以得到网络流图中的最大流,一个网络流图中最大流的流量max_flow是唯一的,但是达到最大流量max_flow时每条边上的流量分配f是不唯一的。
如果给网络流图中的每条边都设置一个费用cost,表示单位流量流经该边时会导致花费cost。那么在这些流量均为max_flow的流量分配f中,存在一个流量总花费最小的最大流方案。
即 min{sum(cost(i, j)*f(i,j) | (i, j)属于方案f中的边, f(i,j)为 边(i,j)上的流量, f为某一个最大流方案}。此即为最小费用最大流
。
算法思想
采用贪心的思想,每次找到一条从源点到达汇点的路径,增加流量,且该条路径满足使得增加的流量的花费最小,直到无法找到一条从源点到达汇点的路径,算法结束。
由于最大流量有限,每执行一次循环流量都会增加,因此该算法肯定会结束,且同时流量也必定会达到网络的最大流量;同时由于每次都是增加的最小的花费,即当前的最小花费是所有到达当前流量flow时的花费最小值,因此最后的总花费最小。
求解步骤
(1)找到一条从源点到达汇点的“距离最短”的路径,“距离”使用该路径上的边的单位费用之和来衡量。
(2)然后找出这条路径上的边的容量的最小值f,则当前最大流max_flow扩充f,同时当前最小费用min_cost扩充 f*min_dist(s,t)。
(3)将这条路径上的每条正向边的容量都减少f,每条反向边的容量都增加f。
(4)重复(1)--(3)直到无法找到从源点到达汇点的路径。
这一段话让我受益匪浅,注意点以下:
需要注意几点:
1、注意超级源点和超级终点的建立。
2、初始化时,正向边的单位流量费用为cost[u][v],那么反向边的单位流量费用就为-cost[u][v]。因为回流费用减少。
3、费用cost数组和容量cap数组每次都要初始化为0。
求解从源点到汇点的“最短”路径时,由于网络中存在负权边,因此使用SPFA来实现。
这道题的是在工作安排里的
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<iostream> 5 #include<cstring> 6 #include<queue> 7 using namespace std; 8 9 const int INF=1e9+7,NN=257*2+7,MM=200007; 10 11 int n,m,S,T; 12 int cnt,head[NN]={0},next[MM]={0},rea[MM]={0},val[MM]={0},cost[MM]={0}; 13 int dis[NN]={0},flag[NN]={0},num[NN]={0},a[NN][NN]={0},E[NN][7]={0},W[NN][7]={0}; 14 struct Node 15 { 16 int e,fa; 17 void init(){e=fa=-1;} 18 }pre[NN]; 19 20 void add(int u,int v,int fee,int fare) 21 { 22 cnt++; 23 next[cnt]=head[u]; 24 head[u]=cnt; 25 rea[cnt]=v; 26 val[cnt]=fee; 27 cost[cnt]=fare; 28 } 29 void build() 30 { 31 for (int i=1;i<=m;i++) 32 for (int j=1;j<=n;j++) 33 if (a[i][j]) add(i,j+m,INF,0),add(j+m,i,0,0); 34 for (int i=1;i<=m;i++) 35 for (int j=1;j<=num[i]+1;j++) 36 add(S,i,E[i][j]-E[i][j-1],W[i][j]),add(i,S,0,-W[i][j]); 37 } 38 bool Spfa() 39 { 40 for (int i=1;i<=n+m+2;i++) 41 { 42 flag[i]=0; 43 dis[i]=INF; 44 pre[i].init(); 45 } 46 dis[S]=0,flag[S]=1; 47 queue<int>q; 48 while(!q.empty()) q.pop(); 49 q.push(S); 50 while(!q.empty()) 51 { 52 int u=q.front(); 53 q.pop(); 54 for (int i=head[u];i!=-1;i=next[i]) 55 { 56 int v=rea[i],fee=cost[i]; 57 if ((dis[v]>dis[u]+fee)&&val[i]>0) 58 { 59 dis[v]=dis[u]+fee; 60 pre[v].e=i; 61 pre[v].fa=u; 62 if (flag[v]==0) 63 { 64 flag[v]=1; 65 q.push(v); 66 } 67 } 68 } 69 flag[u]=0; 70 } 71 if (dis[T]==INF) return 0; 72 else return 1; 73 } 74 long long MFMC() 75 { 76 long long res=0,flow=0; 77 while (Spfa()) 78 { 79 int x=INF; 80 for (int i=T;pre[i].fa!=-1;i=pre[i].fa) 81 { 82 int e=pre[i].e; 83 x=min(x,val[e]); 84 } 85 flow+=x,res=(long long)(res+dis[T]*x); 86 for (int i=T;pre[i].fa!=-1;i=pre[i].fa) 87 { 88 int e=pre[i].e; 89 val[e]-=x,val[e^1]+=x; 90 } 91 } 92 return res; 93 } 94 int main() 95 { 96 cnt=1; 97 memset(head,-1,sizeof(head)); 98 scanf("%d%d",&m,&n); 99 S=m+n+1,T=n+m+2; 100 int x; 101 for (int i=1;i<=n;i++) 102 { 103 scanf("%d",&x); 104 add(i+m,T,x,0),add(T,i+m,0,0); 105 } 106 for (int i=1;i<=m;i++) 107 for (int j=1;j<=n;j++) 108 scanf("%d",&a[i][j]); 109 for (int t=1;t<=m;t++) 110 { 111 scanf("%d",&num[t]); 112 for (int i=1;i<=num[t];i++) 113 scanf("%d",&E[t][i]); 114 E[t][num[t]+1]=INF; 115 for (int i=1;i<=num[t]+1;i++) 116 scanf("%d",&W[t][i]); 117 } 118 build(); 119 printf("%lld\n",MFMC()); 120 }