码迷,mamicode.com
首页 > 其他好文 > 详细

网络流——最小费用最大流模板

时间:2019-08-09 17:29:54      阅读:83      评论:0      收藏:0      [点我收藏+]

标签:include   name   标记   区别   turn   opened   head   mat   algorithm   

 

 

#zkw费用流#

参考网址: https://artofproblemsolving.com/community/c1368h1020435

zkw大佬的改进:①在dfs的时候可以实现多路增广②KM算法节省SPFA时间(然而我这里没有KM,要问为什么,当然是因为我不会了orz);

but,参考了另外的博客,修修补补又三年~

技术图片
  1 #include <algorithm>
  2 #include <iostream>
  3 #include <cstring>
  4 #include <cstdio>
  5 #include <queue>
  6 #include <cmath>
  7 using namespace std;
  8 const int maxv= 200010;
  9 const int maxe= 2000010;
 10 const int INF= 0x3f3f3f3f;
 11 
 12 struct ENode
 13 {
 14     int to;
 15     int c;
 16     int f;
 17     int Next;
 18 };
 19 ENode edegs[maxe];
 20 int Head[maxv], tnt;
 21 void init()
 22 {
 23     memset(Head, -1, sizeof(Head));
 24     tnt= -1;
 25 }
 26 void Add_ENode(int a, int b, int _c, int _f)
 27 {
 28     ++ tnt;
 29     edegs[tnt].to= b;
 30     edegs[tnt].c= _c;
 31     edegs[tnt].f= _f;
 32     edegs[tnt].Next= Head[a];
 33     Head[a]= tnt;
 34     ++ tnt;
 35     edegs[tnt].to= a;
 36     edegs[tnt].c= 0;
 37     edegs[tnt].f= -_f;
 38     edegs[tnt].Next= Head[b];
 39     Head[b]= tnt;
 40 }
 41 
 42 bool vis[maxv];  //访问标记
 43 int dist[maxv];  //每个点的距离标号,其实就是dis[];
 44 inline bool spfa(int s, int t)
 45 {
 46     memset(vis, 0, sizeof(vis));
 47     memset(dist, INF, sizeof(dist));
 48     dist[t]= 0;
 49     vis[t]= 1;//首先SPFA我们维护距离标号的时候要倒着跑,这样可以维护出到终点的最短路径
 50     deque<int> q;
 51     q.push_back(t);//使用了SPFA的SLF优化(SLF可以自行百度或Google)
 52 
 53     while(!q.empty())
 54     {
 55         int u= q.front();
 56         q.pop_front();
 57         for(int k= Head[u]; k!= -1; k=edegs[k].Next)
 58         {
 59             int v= edegs[k].to;
 60             if(edegs[k^1].c&& dist[v]> dist[u]- edegs[k].f)
 61             {
 62                 /*首先c[k^1]是为什么呢,因为我们要保证正流,但是SPFA是倒着跑的,
 63                 所以说我们要求c[k]的对应反向边是正的,这样保证走的方向是正确的*/
 64                 dist[v]= dist[u]- edegs[k].f;
 65                 /*因为已经是倒着的了,我们也可以很清楚明白地知道建边的时候反向边的边权是负的,
 66                 所以减一下就对了(负负得正)*/
 67                 if(!vis[v])
 68                 {
 69                     vis[v]= 1;
 70                     /*SLF优化*/
 71                     if(! q.empty()&& dist[v]< dist[q.front()])q.push_front(v);
 72                     else q.push_back(v);
 73                 }
 74             }
 75         }
 76         vis[u]=0;
 77     }
 78     return dist[s]< INF;//判断起点终点是否连通
 79 }
 80 
 81 int ans; //ans:费用答案
 82 inline int dfs_flow(int now, int c_max, int t)
 83 {
 84     /*这里就是进行増广了,一次dfs进行了多次增广*/
 85     if(now== t)
 86     {
 87         vis[t]=1;
 88         return c_max;
 89     }
 90     int ret= 0, a;
 91     vis[now]=1;
 92 
 93     for(int k=Head[now]; k!= -1; k= edegs[k].Next)
 94     {
 95         int v= edegs[k].to;
 96         if(! vis[v]&& edegs[k].c&& dist[v]== dist[now]- edegs[k].f)
 97         {
 98             /*这个条件就表示这条边可以进行増广*/
 99             a= dfs_flow(v, min(edegs[k].c, c_max- ret), t);
100             if(a)
101             {
102                 /*累加答案,加流等操作都在这了*/
103                 ans+= a* edegs[k].f; /*流过时按流量单位计费*/
104                 //ans+= edegs[k].f; /*流过时费用固定*/
105                 /**注意上面两句指令的区别*/
106                 edegs[k].c-= a;
107                 edegs[k^1].c+= a;
108                 ret+= a;
109             }
110             if(ret== c_max)break;
111         }
112     }
113     return ret;
114 }
115 
116 inline int Min_Cost_Flow(int s, int t)
117 {
118     int flow= 0;
119     ans= 0;  //费用清零
120     while(spfa(s, t))
121     {
122         /*判断起点终点是否连通,不连通说明满流,做完了退出*/
123         vis[t]= 1;
124         while(vis[t])
125         {
126             memset(vis, 0, sizeof vis);
127             flow+= dfs_flow(s, INF, t);//一直増广直到走不到为止(这样也可以省时间哦)
128         }
129     }
130     return flow;//这里返回的是最大流,费用的答案在ans里
131 }
132 int main()
133 {
134     int n, m, s, t;
135     scanf("%d %d %d %d", &n, &m, &s, &t);
136     init();
137     for(int i= 0; i< m; i ++)
138     {
139         int a, b, c, f;
140         scanf("%d%d%d%d", &a, &b, &c, &f);
141         Add_ENode(a, b, c, f);
142     }
143     printf("%d ", Min_Cost_Flow(s, t));
144     printf("%d\n", ans);
145     return 0;
146 }
View Code

 

 

 

 

 

 

end;

网络流——最小费用最大流模板

标签:include   name   标记   区别   turn   opened   head   mat   algorithm   

原文地址:https://www.cnblogs.com/Amaris-diana/p/11328332.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!