标签: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 }
end;
标签:include name 标记 区别 turn opened head mat algorithm
原文地址:https://www.cnblogs.com/Amaris-diana/p/11328332.html