最大流 Ford-Fulkerson

  证明什么的已经有很多了,就是蒟蒻打个板子看看有什么容易写错的。。。

  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       看上去好一点?大概就这样了

猜你喜欢

转载自www.cnblogs.com/Colossus/p/10803831.html