证明什么的已经有很多了,就是蒟蒻打个板子看看有什么容易写错的。。。
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 const int maxN = 1e4 + 2, maxM = 2e5 + 2; 5 int ne = 0, start[maxN] = {0}, n, m, s, t, pre[maxN] = {0}; 6 long long F = 0; 7 bool mark[maxN] = {0}; 8 int maxx(int x, int y) //手写大小 9 {return x > y ? x : y;} 10 int minn(int x, int y) 11 {return x < y ? x : y;} 12 struct queue //手写队列 13 { 14 int a[maxM], l, r; 15 queue() 16 {l = r = 0;} 17 bool empty() 18 {return l >= r;} 19 void push(int x) 20 {a[r++] = x;} 21 void pop() 22 {++l;} 23 int front() 24 {return a[l];} 25 void clear() 26 {l = r = 0;} 27 } q; 28 struct edge 29 { 30 int from, to, next, weight, bound; //这里bound存的是这条边反向边的下标(当然有更好的写法啦见下) 31 edge() 32 {from = to = next = weight = 0;} //一般邻接表才不会记from(始点),后面方便 (话说from明明不是关键字啊怎么蓝了。。。) 33 } ed[maxM]; 34 void adde(int x, int y, int z) 35 { 36 ++ne; 37 ed[ne].from = x; 38 ed[ne].to = y; 39 ed[ne].weight = z; 40 ed[ne].next = start[x]; 41 start[x] = ne; 42 ++ne; //反向边 43 ed[ne].from = y; 44 ed[ne].to = x; 45 ed[ne].weight = 0; //注意:这里我们加反向边的目的是要让我们有一个”反悔“的机会,所以边权是0(一开始写成了-z,这样后面用>0做判断时会有问题) 46 ed[ne].next = start[y]; 47 start[y] = ne; 48 ed[ne].bound = ne-1; 49 ed[ne-1].bound = ne; 50 } 51 void init() //初始化 52 { 53 int inpa, inpb, inpc; 54 scanf("%d%d%d%d",&n,&m,&s,&t); 55 for(int i = 0; i < m; i++) 56 { 57 scanf("%d%d%d",&inpa,&inpb,&inpc); 58 adde(inpa,inpb,inpc); 59 } 60 } 61 bool find_path() //找增广路 62 { 63 int temp; 64 q.clear(); //看这就是手写队列的好处(滑稽 65 for(int i = 1; i <= n; i++) 66 mark[i] = 0; 67 q.push(s); 68 while(!q.empty()) //经典BFS 69 { 70 temp = q.front(); 71 q.pop(); 72 for(int e = start[temp]; e; e = ed[e].next) 73 if(ed[e].weight > 0 && !mark[ed[e].to]) 74 { 75 pre[ed[e].to] = e; // 这里前驱记边不记点的好处就是最后减边权的时候方便 76 q.push(ed[e].to); 77 mark[ed[e].to] = 1; 78 if(ed[e].to == t) 79 return 1; 80 } 81 } 82 } 83 bool proc() // 这段就是Ford-Fulkerson了,或者优化后叫Edmonds-Karp 84 { 85 int temp, minis; 86 if(find_path()) //找增广路去 87 { 88 temp = s; 89 minis = 2147483647; 90 for(int v = t; v != s; v = ed[pre[v]].from) //从汇点往前找,这里就体现邻接表里的from和前驱pre记边的好处了 91 minis = minn(minis,ed[pre[v]].weight); 92 for(int v = t; v != s; v = ed[pre[v]].from) 93 { 94 ed[pre[v]].weight -= minis; 95 ed[ed[pre[v]].bound].weight += minis; 96 } 97 F += minis; 98 return 1; 99 } 100 return 0; 101 } 102 int main() 103 { 104 init(); 105 while(proc()); 106 printf("%d\n",F); 107 return 0; 108 }
当然,用bound记反向边显得有点蠢,所以我们为什们不用一些简单的方式呢?比如说,既然一组反向边的下标是相邻的,那么从2开始编号然后xor1即可
这样定义的话就要 ne=1 了,(加边函数就能省好多行啊),以及95行
ed[pre[v]^1].weight += minis;
^1 是不是比 .bound 看上去好一点?大概就这样了