网络流之费用最小最大流

前不久看了许久的费用流,也没看懂,然后看了农民伯伯的一篇论文后就忽然明白了,十分简单。

最小费用最大流

    通过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 }

 

posted @ 2017-07-25 14:39  Kaiser-  阅读(1077)  评论(0编辑  收藏  举报