网络流--最小费用最大流MCMF算法

来源:互联网 发布:仙侠世界大网络下载 编辑:程序博客网 时间:2024/06/05 07:28

转载:http://www.cnblogs.com/Missa/archive/2013/04/19/3030512.html

在一个网络中每段路径都有“容量”和“费用”两个限制的条件下,此类问题的研究试图寻找出:流量从A到B,如何选择路径、分配经过路径的流量,可以达到所用的费用最小的要求。如n辆卡车要运送物品,从A地到B地。由于每条路段都有不同的路费要缴纳,每条路能容纳的车的数量有限制,最小费用最大流问题指如何分配卡车的出发路径可以达到费用最低,物品又能全部送到。

复制代码
 1 //最小费用最大流模版.求最大费用最大流建图时把费用取负即可。 2 //无向边转换成有向边时需要拆分成两条有向边。即两次加边。 3 const int maxn = 1010; 4 const int maxm = 1000200; 5 const int inf = 1000000000; 6 struct Edge 7 { 8     int v, cap, cost, next; 9 }p[maxm << 1];10 int e, sumFlow, n, m, st, en;11 int head[maxn], dis[maxn], pre[maxn];12 bool vis[maxn];13 void init()14 {15     e=0;16     memset(head,-1,sizeof(head));17 }18 void addEdge(int u,int v,int cap,int cost)19 {20     p[e].v = v; p[e].cap = cap; p[e].cost = cost;21     p[e].next = head[u]; head[u] = e++;22     p[e].v = u; p[e].cap = 0; p[e].cost = - cost;23     p[e].next = head[v]; head[v] = e++;24 }25 bool spfa(int s,int t, int n)26 {27     int u,v;28     queue<int>q;29     memset(vis,false,sizeof(vis));30     memset(pre,-1,sizeof(pre));31     for(int i = 0; i <= n; ++i) 32         dis[i] = inf;33     vis[s] = true;34     dis[s] = 0;35     q.push(s);36     while(!q.empty())37     {38         u = q.front();39         q.pop();40         vis[u] = false;41         for(int i = head[u]; i != -1; i = p[i].next)42         {43             v = p[i].v;44             if(p[i].cap && dis[v] > dis[u] + p[i].cost)45             {46                 dis[v] = dis[u] + p[i].cost;47                 pre[v] = i;48                 if(!vis[v])49                 {50                     q.push(v);51                     vis[v]=true;52                 }53             }54         }55      }56      if(dis[t] == inf) 57          return false;58      return true;59 }60 int MCMF(int s,int t,int n)61 {62     int flow = 0; // 总流量63     int minflow, mincost;64     mincost=0;65     while(spfa(s,t,n))66     {67         minflow = inf + 1;68         for(int i = pre[t]; i != -1; i = pre[p[i^1].v])69             if(p[i].cap < minflow)70                 minflow = p[i].cap;71         flow += minflow;72         for(int i=pre[t];i!=-1;i=pre[p[i^1].v])73         {74             p[i].cap -= minflow;75             p[i^1].cap += minflow;76         }77         mincost += dis[t] * minflow;78     }79     sumFlow = flow; // 最大流80     return mincost;81 }

如果理解了最大流连续增广路算法的思维, 理解这个算法还是很简单的。

结构体存储信息:

分别为边的起点、终点、容量、当前流量、费用、下一条边的编号。

[cpp] view plain copy
  1. struct Edge  
  2. {  
  3.     int from, to, cap, flow, cost, next;  
  4. };  
  5. Edge edge[MAXM];  
  6. int head[MAXN], edgenum;  


算法实现过程:

1,每次查找是否 存在源点到汇点的可增广路径,并用pre[ i ]记录路径上到达点i 的 边 的编号。这里的路径指的是 —— 源点到汇点的最短路(边权为费用) 且 路径上所有边都不能满流;

2,若存在这样的路径,从汇点沿着pre数组向前找,找到该路径上可以增广的流量Min(当然是所有边中剩余流量最小的)。再从汇点遍历一次,正向边增加流量Min,反向边减少Min,总费用累加Min * edge[i].cost,总流量累加Min。

模板:

建图没有写,毕竟图论建图没有模板 o(╯□╰)o

[cpp] view plain copy
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <queue>  
  4. #include <stack>  
  5. #include <vector>  
  6. #include <cmath>  
  7. #include <cstdlib>  
  8. #include <algorithm>  
  9. #define MAXN 200+10  
  10. #define MAXM 80000+100  
  11. #define INF 0x3f3f3f3f  
  12. using namespace std;  
  13. struct Edge  
  14. {  
  15.     int from, to, cap, flow, cost, next;  
  16. };  
  17. Edge edge[MAXM];  
  18. int head[MAXN], edgenum;  
  19. int pre[MAXN];//记录增广路径上 到达点i的边的编号  
  20. int dist[MAXN];  
  21. bool vis[MAXN];  
  22. int N, M;//点数 边数  
  23. int source, sink;//超级源点 超级汇点  
  24. void init()  
  25. {  
  26.     edgenum = 0;  
  27.     memset(head, -1, sizeof(head));  
  28. }  
  29. void addEdge(int u, int v, int w, int c)  
  30. {  
  31.     Edge E1 = {u, v, w, 0, c, head[u]};  
  32.     edge[edgenum] = E1;  
  33.     head[u] = edgenum++;  
  34.     Edge E2 = {v, u, 0, 0, -c, head[v]};  
  35.     edge[edgenum] = E2;  
  36.     head[v] = edgenum++;  
  37. }  
  38. bool SPFA(int s, int t)//寻找花销最少的路径  
  39. {  
  40.     //跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流  
  41.     //若存在 说明可以继续增广,反之不能  
  42.     queue<int> Q;  
  43.     memset(dist, INF, sizeof(dist));  
  44.     memset(vis, falsesizeof(vis));  
  45.     memset(pre, -1, sizeof(pre));  
  46.     dist[s] = 0;  
  47.     vis[s] = true;  
  48.     Q.push(s);  
  49.     while(!Q.empty())  
  50.     {  
  51.         int u = Q.front();  
  52.         Q.pop();  
  53.         vis[u] = false;  
  54.         for(int i = head[u]; i != -1; i = edge[i].next)  
  55.         {  
  56.             Edge E = edge[i];  
  57.             if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//可以松弛 且 没有满流  
  58.             {  
  59.                 dist[E.to] = dist[u] + E.cost;  
  60.                 pre[E.to] = i;//记录前驱边 的编号  
  61.                 if(!vis[E.to])  
  62.                 {  
  63.                     vis[E.to] = true;  
  64.                     Q.push(E.to);  
  65.                 }  
  66.             }  
  67.         }  
  68.     }  
  69.     return pre[t] != -1;//可达返回true  
  70. }  
  71. void MCMF(int s, int t, int &cost, int &flow)  
  72. {  
  73.     flow = 0;//总流量  
  74.     cost = 0;//总费用  
  75.     while(SPFA(s, t))//每次寻找花销最小的路径  
  76.     {  
  77.         int Min = INF;  
  78.         //通过反向弧 在源点到汇点的最少花费路径 找最小增广流  
  79.         for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])  
  80.         {  
  81.             Edge E = edge[i];  
  82.             Min = min(Min, E.cap - E.flow);  
  83.         }  
  84.         //增广  
  85.         for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])  
  86.         {  
  87.             edge[i].flow += Min;  
  88.             edge[i^1].flow -= Min;  
  89.             cost += edge[i].cost * Min;//增广流的花销  
  90.         }  
  91.         flow += Min;//总流量累加  
  92.     }  
  93. }  
  94. int main()  
  95. {  
  96.     while(scanf("%d%d", &N, &M), N||M)  
  97.     {  
  98.         init();  
  99.         getMap();//建图   
  100.         int cost, flow;//最小费用 最大流  
  101.         MCMF(source, sink, cost, flow);  
  102.         printf("%d %d\n", cost, flow);//最小费用 最大流  
  103.     }  
  104.     return 0;  
  105. }  
原创粉丝点击